Sleep and Wake Scripting OSX

There have been many times where I thought to myself — I wish my computer would do X when it goes to sleep and Y when it wakes up. There are many applications for this, some include:

  • Mounting an SSH filesystem depending on WiFi connection
  • Have the computer greet you and dismiss you (very nerdy)
  • Disconnect/Kill some application that always hangs on sleep
  • Move files from one folder to another
  • Tweet

So how is this nerdy matter accomplished? It’s actually pretty trivial. Familiar with bash? Then you’re on the way to sleep/wake success!

Installing Sleepwatcher

Head over to http://www.bernhard-baehr.de/ and download a copy of Sleepwatcher 2.2. The installation instructions are pretty simple:

$ sudo mkdir -p /usr/local/sbin /usr/local/share/man/man8
$ sudo cp ~/Desktop/sleepwatcher_2.2/sleepwatcher /usr/local/sbin
$ sudo cp ~/Desktop/sleepwatcher_2.2/sleepwatcher.8 /usr/local/share/man/man8

Once installed, run it in daemon mode:

$ /usr/local/sbin/sleepwatcher -d --sleep /path/to/your/sleepscript --wakeup /path/to/your/wakeupscript

If testing is needed run it in verbose mode:

$ /usr/local/sbin/sleepwatcher --verbose --sleep /path/to/your/sleepscript --wakeup /path/to/your/wakeupscript

A simple hello/goodbye script

Now that sleepwatcher is installed, let’s make the computer say Welcome back, <yourname> on wake and Goodbye on sleep. Two files are needed for this:

  • sleepscript – script that runs when the computer sleeps
  • wakescript – script that runs when the computer wakes

The scripts can be placed anywhere but the standard place would be to place them in /Users/<username>/bin. The bin folder should contain:

  • /Users/<username>/bin/sleepscript
  • /Users/<username>/bin/wakescript

The scripts should contain:

Sleep Script

function say_goodbye {
	say -v Vicki "goodbye"
}
say_goodbye

Wake Script

function say_hello {
	say -v Vicki "Welcome back, username"
}
say_hello

Make sure to chmod the scripts to 775. Next, launch the daemon to test:

$ /usr/local/sbin/sleepwatcher -d --sleep /Users/<username>/bin/sleepscript --wakeup /Users/<username>/bin/wakescript

Demo

Here is a video demo showing my Macbook Air saying “Welcome back” and “Goodbye”. 1337!

Happy Coding!

IPv6: Biting the bullet

With IPv4 on the verge of being all used up and its successor IPv6 still in the distance, most companies are going to require massive code, infrastructure, and database changes. But hey, you get 2^128 IP addresses! That’s enough to move this planet for the next 200 years (a wild guess).

IPv4 vs. IPv6

So what’s the difference? IPv4 has 32 bits which means it’s possible to have 4,294,967,296 or 4.2 billion IP addresses. The new and hopefully improved IPv6 has 128 bits. While 32 vs 128 might not seem like a lot as a decimal number, IPv6 boasts a whopping 3.40282367 × 10^38 IP addresses. Lets expand that:

340,282,367,000,000,000,000,000,000,000,000,000,000 (WTF?!)

I don’t even know what that is?! A quick lookup in Google tells me this is 340 undecillion IP address. Hopefully this is enough to last us a while before we have to invest in yet another infrastructure change. So what does an IPv6 address look like? Traditional IPv4 looks like this:

192.168.1.100

The standard 4 octets above form a binary number totaling 32 bits. IPv6 is separated by colons and contains 8 groups of 16 bits in hexadecimal, hence the 128 bits. A typical IPv6 address:

0891:a783:804f:8c8d:033a:2ae2:16dc:b1ea

In it’s compressed form (removing the leading zeros) it looks like:

891:a783:804f:8c8d:33a:2ae2:16dc:b1ea

You can find more on how to compress IPv6 at wikipedia. Let’s focus on storing these suckers in the database.

Storing IPv6 Addresses

I am only going to focus on MySQL because that’s what I have been using, but there are two data types used to store IPv4 and IPv6 addresses in the same field (there are more, these are most common):

  • VARCHAR(39) – 39 characters allowing for both full IPv4 and IPv6 addresses to be stored
  • VARBINARY(16) – binary data up to 16 bytes or 128 bits

Both are suitable but have different use cases. If you want to only store the IP for simple whois lookups, DNS checks, mapping, or for presentation (UI) then VARCHAR is the way to go. Need to perform range operations (from xxx bits to xxx bits) on the data? VARBINARY is your friend. Plus, binary is cool.

Storing an ip address in VARBINARY is pretty trivial. This simple insert statement will do the trick:

INSERT INTO hosts (ip_address) VALUES (INET6_PTON('your_ip_address'));

Or an update statement:

UPDATE hosts SET ip_address = INET6_PTON('your_ip_address');

Storage is simple, unless you want to make it complex, but sticking to the KISS principle usually works best. The package for INET6_PTON is developed and maintained by Watchmouse. I haven’t been successful in getting the package to work in OSX, but hopefully I’ll have that resolved soon and post an update. In ubuntu, like always, it’s cake. In MySQL 5.6+ the packages are built-in.

IPv6 in Code

Now that IPv6 is ready to go on the database side, how do we validate before entering data? I’m going to explain this in server side code. If you need javascript validation, try Beaugunderson’s IPv6 Library. It works in both browser and Nodejs.

Let’s start with Python. Using a package called IPy we can test some IP addresses like so:

>>> import IPy
>>> ip = IPy.IP('0891:a783:804f:8c8d:033a:2ae2:16dc:b1ea')
>>> ip.version()
6
>>> ip = IPy.IP('0891:a783:804f:8c8d:033a:2ae2:16dc:b1ea/128')
>>> ip.version()
6
>>> ip = IPy.IP('192.168.1.100')
>>> ip.version()
4
>>> ip = IPy.IP('thisipisnotvalid')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/glenbot/.virtualenv/blogtests/lib/python2.7/site-packages/IPy.py", line 230, in __init__
    (self.ip, parsedVersion) = parseAddress(ip)
  File "/Users/glenbot/.virtualenv/blogtests/lib/python2.7/site-packages/IPy.py", line 1164, in parseAddress
    ret = long(ipstr, 10)
ValueError: invalid literal for long() with base 10: 'thisipisnotvalid'

Notice that it throws an exception when an IP is invalid. To test validation, writing a simple function will do the trick:

from IPy import IP

def ip_is_valid(ip_address):
    try:
        ip = IP(ip_address)
        return True
    except:
        return False

def get_version(ip_address):
    if ip_is_valid(ip_address):
        return IP(ip_address).version()
    return None

Validation in PHP is built-in. Using the filter_var function we can accomplish validation:

$is_ipv4 = filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
$is_ipv6 = filter_var($ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6);

if ($is_ipv6) {
    // do something here
}

if ($is_ipv4) {
    // do something here
}

This will not validate IP addresses with a prefix notation ex. 0891:a783:804f:8c8d:033a:2ae2:16dc:b1ea/128. For this, the PEAR IPv6 package is needed. In reality you could just split out the /128 but using the package is probably the safe route.

$net_ipv4  = new Net_IPv4();
$net_ipv6  = new Net_IPv6();

$is_ipv4 = !PEAR::isError($net_ipv4->parseAddress($ip_addr));
$is_ipv6 = $net_ipv6->checkIPv6($ip_addr);

if ($is_ipv6) {
    // do something here
}

if ($is_ipv4) {
    // do something here
}

Happily Ever After …

Hopefully this will get you started on your journey towards becoming an IPv4/6 validating Pirate! It’s somewhat trivial and biting the bullet now will definitely pay off. Happy coding!

Update and backup WordPress sites on WebFaction

If you run multiple WordPress blogs on WebFaction it can be a chore to update. You know the song and dance: visit the admin panel, click upgrade, wait for it to install, and repeat for the next blog. I decided to write a shell script that helps out with this process. It assumes you use the default path WebFaction sets up for you. E.g /home/username/webapps/wordpress-site/. Place it in your home folder.

The script will:

  • Download the latest WordPress
  • Backup the database
  • Backup the site
  • Copy over new WordPress files

The script will not:

  • Upgrade the database
  • Upgrade your plugins
  • Compensate for non standard installations
  • Win the Lottery

As noted above, in some cases you may need to upgrade the database. That can be done by visiting http://yoursite.com/wp-admin/ and clicking the upgrade button (You don’t have to login).

The script is below or you can download it here.

#!/bin/bash
# Backup and update wordpress sites on webfaction
# Author: Glen Zangirolami
# URL: http://twitter.com/glenbot

# Get a list of sites under webapps
WEB_APPS=$HOME/webapps/*

# Set backup paths
DB_BACKUPS=$HOME/wordpress_backups/databases
SITE_BACKUPS=$HOME/wordpress_backups/sites

# Set new wordpress extraction path
WP=$HOME/wordpress_extaction

# Download wordpress and extract
wget http://wordpress.org/latest.tar.gz
mkdir -p $WP
tar -C $WP -xzf latest.tar.gz
rm latest.tar.gz

# Make the database backup path
if [ ! -d $DB_BACKUPS ]; then
  mkdir -p $DB_BACKUPS
fi

# Make the site backup path
if [ ! -d $SITE_BACKUPS ]; then
  mkdir -p $SITE_BACKUPS
fi

# Loop through the apps and:
# 1. Backup the database
# 2. Backup the site directory
# 3. Copy new wordpress files
for APP in $WEB_APPS
do
  WP_CONFIG=$APP/wp-config.php
  APP_NAME=`echo $APP | cut -d \/ -f5`
  IS_WP=0

  if [ -f $WP_CONFIG ]; then
    IS_WP=1
  fi

  if [ $IS_WP -eq 1 ]; then
    DB_NAME=`cat $WP_CONFIG | grep -P -o "define\(\'DB_NAME',\s*\'([^\']+)\'\);" | cut -d \' -f4`
    DB_USER=`cat $WP_CONFIG | grep -P -o "define\(\'DB_USER',\s*\'([^\']+)\'\);" | cut -d \' -f4`
    DB_PASSWORD=`cat $WP_CONFIG | grep -P -o "define\(\'DB_PASSWORD',\s*\'([^\']+)\'\);" | cut -d \' -f4`
    DB_HOST=`cat $WP_CONFIG | grep -P -o "define\(\'DB_HOST',\s*\'([^\']+)\'\);" | cut -d \' -f4`
    echo "Backing up database for $APP_NAME ..."
    mysqldump -h $DB_HOST -u $DB_USER -p$DB_PASSWORD $DB_NAME > $DB_BACKUPS/$DB_NAME.sql

    echo "Backing up site $APP_NAME ..."
    tar -czf $SITE_BACKUPS/$APP_NAME.tar.gz $APP/*

    echo "Copying over new wordpress to $APP_NAME ..."
    cp -rf $WP/* $APP
  fi
done

Happy coding!

Fork me on GitHub