Functional "WOL" implementation

This is in reply to the entry “Wake On Lan (WOL) support for My Book Live”. This entry has been set to status “Not planned” by WD staff and has apparently been locked. Which, if you permit me saying, is a pretty dumb approach, as it doesn’t allow users to post a working solution even if WD staff is not interested in finding one.

The solution posted in the other topic by stefan29 almost works.

If one replaces the shutdown command with hdparm -y /dev/sda it becomes a fully functional solution, and indeed better than actual WOL.

hdparm -y will immediately cause the drive to go into low power standby mode (spin down), unless or until there is disk activity.

As soon as you access data on My Book again and the data is not in the buffer cache (i.e. disk access is necessary), the drive spins up again. There is obviously a small delay (2-3 seconds), but this is not nearly the same impact as when booting (30-40 seconds). No pulling cables, no special tricks necessary.

My “My Book Live” draws 7W with the disk running, 9.2 to 9.5W with disk activity, and 5W with the disk turned off. However, it also draws 5W when turned off completely  (uh… AC adapter efficiency, anyone?), so there is really not much of a point of going any further than spinning down the disk.

Modified cron script (inspired by stefan29’s original, stripped out comments and such):

#!/bin/sh

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/bin:/opt/sbin

MYIP=$( ifconfig eth0 | grep "inet " | cut -d: -f2 | cut -d " " -f1 )
NETWORK=$( echo "$MYIP" | cut -d. -f-3 )
CLIENTS=$NETWORK.0/24
ROUTERIP=$( route -n | grep ^0.0.0.0 | awk '{print $2;}' ),$NETWORK.254

TIMEOUTFILE=/tmp/spindown_imminent

if nmap -snPP $CLIENTS --exclude $MYIP,$ROUTERIP | grep -q "\(0 hosts up\)" ; then

	if test -f $TIMEOUTFILE ; then
		rm $TIMEOUTFILE
		hdparm -y /dev/sda
	else
		touch $TIMEOUTFILE
	fi
else
	if test -f $TIMEOUTFILE ; then
		rm $TIMEOUTFILE
	fi
fi

The script can be further refined by adding 4 lines at the top:

if hdparm -C /dev/sda | grep -q "standby" ; then
    exit 0
fi
sync

This ensures two things:

  1. If the disk is already sleeping, we do not do unnecessary work (this is not only unnecessary, but it also causes needless network traffic, and there is a small chance that we actually wake the HD).
  2. In case there remain any dirty pages in the buffer cache, we initiate a writeback early (during the minutes between two calls to the script), or as a last resort while nmap is running. This reduces the likelihood of us putting the disk to sleep, only for pdflush to wake it up a few seconds later.

Update:

Added the ability to ignore specific IP addresses (smart switch and printer in my case) so for example if you forget to turn off the printer, it doesn’t prevent the NAS from spinning down as well.

Also made a version that logs status changes, tested over night. This works really well. I’m inclined to say that this can be considered pretty much “production” now.

Stripped down version with logging code removed (not really needed, except for testing that it works!).

If you have any smart switches or similar non-computer-stuff on your LAN that stays powered on 24/7 and that might respond to nmap, put their addresses into $IGNORE, otherwise set $IGNORE to empty.

#!/bin/sh
# 192.168.178.13 = NetGear GS108E
# 192.168.178.14 = HP LaserJet 2050N
IGNORE=192.168.178.13,192.168.178.14

PATH=/bin:/sbin:/opt/bin:/usr/bin:/usr/sbin
MARKER=/tmp/autoshutdown_marker

if hdparm -C /dev/sda | grep -q "standby" ; then
        exit 0
fi

sync

if [[$IGNORE]] ; then
        IGNORE="--exclude $IGNORE"
fi

MYIP=$( ifconfig eth0 | grep "inet " | cut -d: -f2 | cut -d " " -f1 )
NETWORK=$( echo "$MYIP" | cut -d. -f-3 )
CLIENTS=$NETWORK.0/24
ROUTERIP=$( route -n | grep ^0.0.0.0 | awk '{print $2;}' ),$NETWORK.254

if nmap -snPP $CLIENTS $IGNORE --exclude $MYIP,$ROUTERIP | grep -q "\(0 hosts up\)" ; then

        if test -f $MARKER ; then
                rm $MARKER
                sleep 2
                hdparm -y /dev/sda
                exit 0
        else
                touch $MARKER
        fi
else
        if test -f $MARKER ; then
                rm $MARKER
        fi
fi

Original version with logging functionality for debugging (in fact, simply changing $LOGFILE to /dev/null would probably have been more elegant than stripping out the logging):

#!/bin/sh
# ignore HP LaserJet
# ignore NetGear GS108E
IGNORE=192.168.178.13,192.168.178.14

PATH=/bin:/sbin:/opt/bin:/usr/bin:/usr/sbin
MARKER=/tmp/autoshutdown_marker
LOGFILE=/tmp/autoshutdown_log

if hdparm -C /dev/sda | grep -q "standby" ; then
        date +"%Y %m %d %R : [standby]" >> $LOGFILE
        exit 0
fi

sync

if [[$IGNORE]] ; then
        IGNORE="--exclude $IGNORE"
fi

MYIP=$( ifconfig eth0 | grep "inet " | cut -d: -f2 | cut -d " " -f1 )
NETWORK=$( echo "$MYIP" | cut -d. -f-3 )
CLIENTS=$NETWORK.0/24
ROUTERIP=$( route -n | grep ^0.0.0.0 | awk '{print $2;}' ),$NETWORK.254

if nmap -snPP $CLIENTS $IGNORE --exclude $MYIP,$ROUTERIP | grep -q "\(0 hosts up\)" ; then

        if test -f $MARKER ; then
                rm $MARKER
                sleep 2
                date +"%Y %m %d %R : still no hosts up ---> entering standby mode" >> $LOGFILE
                hdparm -y /dev/sda
                exit 0
        else
                date +"%Y %m %d %R : no hosts up" >> $LOGFILE
                touch $MARKER
        fi
else
        if test -f $MARKER ; then
                date +"%Y %m %d %R : hosts detected" >> $LOGFILE
                rm $MARKER
        fi
fi

Output from last night, running */5 * * * * from crontab, comments in bold :

NAS:~# cat /tmp/autoshutdown_log
2013 01 23 20:30 : no hosts up (turned off computer at 20:28)
2013 01 23 20:35 : still no hosts up ---> entering standby mode
2013 01 23 20:40 : [standby]
2013 01 23 20:45 : [standby]
2013 01 23 20:50 : [standby]
2013 01 23 20:55 : [standby]
2013 01 23 21:00 : [standby]
...
2013 01 24 02:55 : [standby] (cron.daily runs here)
2013 01 24 03:05 : no hosts up
2013 01 24 03:10 : still no hosts up ---> entering standby mode
2013 01 24 03:15 : [standby]
2013 01 24 03:20 : [standby]
2013 01 24 03:25 : [standby]
...
2013 01 24 04:00 : [standby]
2013 01 24 04:05 : no hosts up
2013 01 24 04:10 : still no hosts up ---> entering standby mode
2013 01 24 04:15 : [standby]
2013 01 24 04:20 : [standby]
2013 01 24 04:25 : [standby]
2013 01 24 04:30 : [standby]
2013 01 24 04:35 : [standby]
...
2013 01 24 07:35 : [standby] (turn on computer for work)

As can be seen the disk wakes twice during the night: once when cron.daily runs and once an hour later. cron. daily runs, among other things, htcacheclean, updates apt and aptitude, checks for firmware updates, backs up samba config, and writes ramlog to disk. Further, the aptitude update script sleeps for a random amount of time from 30 min to 1 hour in the middle (for some obscure reason).

What we see happening here is thus entirely well-explained.

Two spin-up/spin/down cycles per night is very acceptable, in my opinion.