Running a Shell Script on Shutdown via launchd

2019-05-27 03:31发布

问题:

Since startup items and rc commands are both deprecated (and in many cases not working at all) on OS X in favour of launchd, I'd like to find out what the correct way would be to setup a shell script that runs on logout/shutdown.

For a startup item it was possible to create a shell script that looked something like:

#!/bin/sh
StartService() {
    echo "Started."
}

StopService() {
    echo "Stopped."
}

RunService "$1"

But the RunService command isn't supported when running a script from launchd, and I'm not sure it's meant to be used anymore anyway.

If possible, I'd like to create a shell script that will be provided to launchd as an on-demand service that will be started near to shutdown and somehow informed that the system is shutting down.

Alternatively, I may need a shell script that is opened on login/system start and remains running (or rather, asleep) until a SIGTERM or other kill signal is received, so that it can run some commands before closing.

回答1:

@gaige thanks for the reply! In the end I went for the following:

#!/bin/sh
StartService() {
    echo "Started"
}

StopService() {
    echo "Stopped"
    exit 0
}

StartService
trap StopService SIGTERM
while true; do
    sleep 86400 &
    wait $!
done

Sleeping for a full day should prevent it from wasting any processing power, though it's not exactly my favourite solution to have something running like that. However, launchd doesn't really have any criteria that seemed like it would allow my script to only run at shutdown. The above instead is run on load (login) and then captures a SIGTERM on termination, logout or shutdown. Note the asynchronous use of sleep, as some environments will wait for the sleep to finish before executing the trap, which is no good as launchd only allows a couple of seconds to respond to SIGTERM before SIGKILL is sent.

As far as I can tell launchd only supports on demand services if they're triggered by a socket or watched path or whatever, which didn't really present a solution for me since it would still require some kind of outside influence.

One beneficial side effect however is that any results I obtained in StartService, such as file pointers etc., can be easily accessed by StopService since it's just in memory, no need for file saving. Just doesn't feel like the most elegant solution, but hey, if it works it's fine!