Using crankd to react to network events12 Jul 2013
Updated 14/7/2013: After Alister’s suggestion, the script now loops over network interfaces up to en19 (hopefully that’s enough!).
So, you’ve heard of this crankd thing, maybe even had a look at it, but have no idea how to get it going? You’re in the right place. I’m by no means an expert on it, having only been playing with it for less than a week, but I already have it running in production running the simple script below. My initial work, and therefore this post was inspired by Gary Larizza’s two articles on the subject.
What is crankd?
It’s part of the PyMacAdmin set of tools that Chris Adams and Nigel Kersten released a while ago. In a nutshell, it runs in the background via a LaunchDaemon and reacts to events on the Mac by running a script or a Python function, class or method. It has loads of events it knows about (application launches, power events, network events etc), but in this case I wanted to run something when there was a network change. Some of our machines never get turned off (and for some reason the Puppet Launch Daemon has crapped out), or aren’t turned on long enough for Puppet or Munki to run. I wanted a script that would run every time the machine came back onto the network, checking if there was an active connection and run Puppet and Munki.
What do I need to do?
There are a few parts that we need to bring together to make this work:
- The crankd.py executable and the supporting files
- A Launch Daemon to start the thing
- A preferences file to tell crankd what to do
- And finally, our custom code
Get and install crankd
First off, you need to grab the current code from GitHub.
git clone https://github.com/acdha/pymacadmin.git
cd into the pymacadmin directory you just cloned and run
That will install the crankd.py executable and it’s supporting files, now for the Launch Daemon to make it start at boot. You’ll need to put the following into a file at
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>KeepAlive</key> <true/> <key>Label</key> <string>com.googlecode.pymacadmin.crankd</string> <key>ProgramArguments</key> <array> <string>/usr/local/sbin/crankd.py</string> </array> <key>RunAtLoad</key> <true/> </dict> </plist>
And set the right ownership and permissions on the plist
So that’s the basics. Now we need to tell crankd what events it should listen to and what it should do.
As we want to call the CrankTools class and the OnNetworkLoad method every time the network changes state, we need to do the following in
/Library/Preferences/com.googlecode.pymacadmin.crankd.plist. To see what other events you can use with crankd, head on over to the GitHub repo.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>SystemConfiguration</key> <dict> <key>State:/Network/Global/IPv4</key> <dict> <key>method</key> <array> <string>CrankTools</string> <string>OnNetworkLoad</string> </array> </dict> </dict> </dict> </plist>
Now for the actual Python code. This is very heavily inspired by Gary Larizza’s work. We’re checking if either en0 or en1 has a valid network connection (as this event is for any network change - both connecting and disconnecting), and if there is a valid connection, run Puppet and then run Munki. This code could easily be modified to run anything you wanted to at the command line (for example a Casper policy). Put the following script in
It’s ok, we’re nearly there! You just need to set the right owner on
CrankTools.py , load the Launch Daemon and we can get testing.
You’re all set. Disconnect your network connection and re-connect. Put your Mac to sleep and wake it up. Each time, there should be a 10 second delay, then a Puppet run followed by a Munki run will happen.
If you’ve modified
CrankTools.py, you can test the changes by running the script directly.
Obviously this is not a good way of deploying crankd - I’ve got a method in the works that will build a package to install this (I currently deploy this with Puppet - I’ll put in a pull request on Gary’s module with my changes when I get chance). I’m also going to be doing more with crankd - possibly some application use monitoring, almost certainly some scripts fired off when a machine wakes and sleeps.