(or "Acting on mount events with launchd")
I recently rediscovered an old toy of mine, a Sony GPS-CS1. A nifty little no-fuss device which simply logs my position, every blah many seconds, until I turn it off again. It's a lot less stalker-y than it sounds; I later match up timestamps on photos with my position at the time.
The downside, is that importing these logs is nowhere near as fuss-free as the rest of the device. I have to pull the logs off the device (my mac just treats it like any other usb disk), and then convert the logs into a format that's more commonly handled by my various bits of software. Simply so that I can play with Google Earth, this ends up being two different formats. Fun stuff.
Starting the job; launchd
So to start automating this, I want a script to run when I plug the gizmo in. To achieve the 'when' part of this, I'm going to let launchd handle everything. So I create a plist, /Users/soneil/Library/LaunchAgents/com.shaunoneil.sonygps.plist, as follows:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" \ "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <dict> <key>Label</key> <string>com.shaunoneil.sonygps</string> <key>Program</key> <string>/Users/soneil/Library/Scripts/sonygps</string> <key>ProgramArguments</key> <array> <string>sonygps</string> </array> <key>WatchPaths</key> <array> <string>/Volumes</string> </array> </dict> </plist>
This is hopefully pretty straight-forward. The fun part is the WatchPaths key, which means /Users/soneil/Library/Scripts/sonygps will be invoked when the contents of '/Volumes' changes.
The gotcha here is that I can't watch /Volumes/SONY_GPS, which is what the gizmo mounts as, as it doesn't exist when launchd starts. So sonygps is going to be started every single time a disk is inserted, and every single time a disk is removed; The script is going to need to be clever about this.
(Sidenote: That's the theory. In reality, it appears to be invoked twice when a disk is inserted, and once when it's removed.)
Getting some work done
There's really not much to this bit. Create & check for lock files, and don't do anything interesting unless you find the disk you're looking for. I've used growlnotify for feedback so I can end & detach the disk without waiting around for UI.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | #!/bin/bash DISK=/Volumes/SONY\ GPS LOCK=/tmp/.sonygps.lck PATH=$PATH:/usr/local/bin # die if locked if [ -f "$LOCK" ]; then exit 0 fi if [ -d "$DISK" ]; then # lock touch "$LOCK" # make sure I exist mkdir -p $HOME/Library/GPS/Logs mkdir -p $HOME/Library/GPS/GPX mkdir -p $HOME/Library/GPS/KML # bring the contents off the disk # (using ~/Library because I can't promise ~/Documents is mounted!) rsync -aq "$DISK/GPS/" $HOME/Library/GPS/Logs/ rm "$DISK"/GPS/*log # Convert nmea to useful formats cd $HOME/Library/GPS/Logs/ for FILE in *log; do KML=$HOME/Library/GPS/KML/${FILE/log/kml} GPX=$HOME/Library/GPS/GPX/${FILE/log/gpx} if [ ! -f $KML ]; then gpsbabel -t -w -i nmea -f $FILE -o kml,units=m -F $KML fi if [ ! -f $GPX ]; then gpsbabel -t -w -i nmea -f $FILE -o gpx -F $GPX fi done # we're done here. hdiutil eject "$DISK" growlnotify -a /Applications/HoudahGPS.app -t "Sony GPS" -m "Import finished" # launchd freaks out if I die too quick/often, so sit around doing bugger # all to preserve launchd's sanity. sleep 30 rm "$LOCK" fi |