OS X application start/stop hook

applicationsscriptterminal

How can I have a script automatically ran when I start or stop an app without using 3rd party tools?

I'm not developing the application myself, it's a general question that applies to any installed apps.

Clarifications: The application is started/quit be any means possible. The script and the application should be ran serially.

Best Answer

Assuming answers to my clarification questions above are that you want:

  1. the script to run anytime a specific application is run, regardless of how or by what process or user, and
  2. the script to run in an asynchronous, non-blocking way (e.g. for logging purposes)

...there may be a few ways to do it. None I can think of are terribly elegant or turnkey and all require some sort of demonized process running as root which act to monitor the system for launch events.

Monitoring exec calls via fs_usage

One possible way is to create a daemon (LaunchDaemon) which runs continuously, monitoring a running fs_usage command for exec events. You can see the kind of information you get by running the following command:

sudo fs_usage -f exec

Here's the relevant line when I opened Safari:

17:30:14.465075  posix_spawn    /Applications/Safari.app/Contents/MacOS/Safari    0.000352   launchd.51630812

Here's the output when I opened ran open /Applications/Maps.app from the command line:

17:35:51.594849  execve       /usr/bin/open                                 0.003680 W bash.51635318
17:35:51.642212  posix_spawn  /Applications/Maps.app/Contents/MacOS/Maps    0.003158 W launchd.51635324

Note that nearly all "Applications" (/Applications/*.app) are launched via posix_spawn by launchd and the file used to launch them is /Applications/FOO.app/Contents/MacOS/FOO (where FOO is the application's name). So, theoretically, you could have your LaunchDaemon monitoring this command and triggering your script whenever it outputs the path to the app you're monitoring:

sudo fs_usage -w -f exec | perl -lne 'm/.*posix_spawn +(.*?) +[0-9.]+ . launchd.\d+/sm && print $1'

Monitoring launchservicesd via lsappinfo

I wasn't terribly happy with the above because it's kinda hacky, watching for filesystem events instead of the launching event that triggered them. And then I realized, "launchd"! Enter Launch Services:

Leading to:

lsappinfo listen +all forever

And VoilĂ , you not only have launching, but also get notified when an app quits. The keys you care about are the following:

  • kLSNotifyApplicationLaunch
  • kLSNotifyApplicationBirth
  • kLSNotifyLaunchFinished
  • kLSNotifyApplicationDeath