Howto: Automatically spin down external usb hard drives in Ubuntu


Spinning down external usb hard drives in Ubuntu doesn’t allways happen automatically in Ubuntu. To force a spindown, you can issue the command

sync
sdparm --flexible --command=stop /dev/sdX &>/dev/null

where you should replace the sdX entry by the correct name of your external hard drive (usually sdb, sdc or similar).
To do this automatically every 30 minutes, save the code below as e.g. ~/bin/spindown/spindown.sh.

# !/bin/sh

# Get new state from diskstats
NEWstate=$(cat /proc/diskstats | grep $1)
echo $NEWstate > NEWstate.txt

# compare md5 sums
md5new=$(md5sum NEWstate.txt | sed 's/ .*//')
md5old=$(md5sum OLDstate.txt | sed 's/ .*//')

# if no changes, power down
if [ "$md5new" = "$md5old" ]; then
	sdparm --flexible --command=stop /dev/$1 &>/dev/null
fi

# Write current state to file
echo $NEWstate > OLDstate.txt

Then, add the entry

*/30 *   * * *   root	sh /home/user/bin/spindown/spindown.sh sdX

to the cron file /etc/crontab. The script automatically checks to see if the drive has been active for the last 5 mins. If not, it forces a spindown.

Share and Enjoy:
  • Digg
  • del.icio.us
  • Facebook
  • Google

21 thoughts on “Howto: Automatically spin down external usb hard drives in Ubuntu

  1. Great post – works really well. Thanks.

    If anybody’s interested, I also added some code at the top of the script to check that it is being run as root…

    if [ "$(id -u)" != "0" ]; then
    echo “This script must be run as root” 1>&2
    exit 1
    fi

    Obviously this is not needed for cron, but useful for testing etc.

  2. hey!

    many thanks for the script.

    for external usb drive compatibility i added the following lines.

    #convert UUID to ID
    disk=$(blkid | grep $1 | cut -c 6-9)

    #check if the disk uuid, or pieces of it were ok
    if [ ! -n $disk ]; then
    echo “no such disk – disk!”
    exit 1
    fi

    cheers

  3. Are md5sum and sed really necessary? Could you not just do a diff between OLDstate.txt and NEWstate.txt?

    [ -z $(diff OLDstate.txt NEWstate.txt) ] && sdparm –flexible –command=stop /dev/$1

    Translation: Spin down when diff returns a zero-length string on comparing OLDstate.txt and NEWstate.txt. This gets rid of a few lines of code and reduces external dependencies.

  4. */5 * * * * root sh /home/user/bin/spindown/spindown.sh sdX

    should the sdX be sda, sdb, sdc etc or should it stay sdX

    thanks

  5. # !/bin/sh

    if [[ -e /dev/$1 ]]; then
    echo $1 Spin power down
    notify-send –icon=’/usr/share/icons/GartoonRedux/scalable/apps/gaim.svg’ “Spinning Powerdown: $1″
    else
    # Get new state from diskstats
    NEWstate=$(cat /proc/diskstats | grep $1)
    echo $NEWstate > /var/ds.state/NEWstate.$1

    # compare md5 sums
    md5new=$(md5sum /var/ds.state/NEWstate.$1 | sed ‘s/ .*//’)
    md5old=$(md5sum /var/ds.state/OLDstate.$1 | sed ‘s/ .*//’)

    # if no changes, power down
    if [ "$md5new" = "$md5old" ]; then
    sdparm –flexible –command=stop /dev/$1 &>/dev/null
    fi

    # Write current state to file
    echo $NEWstate > /var/ds.state/OLDstate.$1

    echo $1 Device Does Not Exist !
    exit 0
    fi

    So… i put a notify in the script and the oldstat,txt is replaced with oldstate.$ so will permit to use it on more than one disk as the extension will be .sdX and made a sort of check if the disk exist, was a good idea and a good job to start wirh, thanks

  6. # !/bin/sh

    if [[ -e /dev/$1 ]]; then
    echo $1 Spin power down
    notify-send –icon=’/usr/share/icons/GartoonRedux/scalable/apps/gaim.svg’ “Spinning Powerdown: $1″
    # Get new state from diskstats
    NEWstate=$(cat /proc/diskstats | grep $1)
    echo $NEWstate > /var/ds.state/NEWstate.$1

    # compare md5 sums
    md5new=$(md5sum /var/ds.state/NEWstate.$1 | sed ‘s/ .*//’)
    md5old=$(md5sum /var/ds.state/OLDstate.$1 | sed ‘s/ .*//’)

    # if no changes, power down
    if [ "$md5new" = "$md5old" ]; then
    sdparm –flexible –command=stop /dev/$1 &>/dev/null
    fi

    # Write current state to file
    echo $NEWstate > /var/ds.state/OLDstate.$1
    else
    echo $1 Device Does Not Exist !
    exit 0
    fi

    ops i posted the wrong one… this one works

  7. @thinmintaddict

    It sounds as if a process is accessing the drive while your are trying to spin it down.
    You can use ”fuser -m /path/to/device” to get a list of processes accessing the device,
    and ”fuser -km /path/to/device” to kill the processes. Check out fuser’s manpage for more options.

  8. yes sdx could be sda sdb sde or so on, seems working fine here,but i found something similar:
    #!/bin/bash
    let a=0

    for i in `seq 0 100`
    do
    let a=`cat /proc/diskstats | grep “0 $1″ | awk ‘{print $(NF-2)}’`+a
    sleep 0.1s
    done
    echo $a
    if [ $a == 0 ]
    then
    sdparm -C stop /dev/$1
    fi
    exit 0

    seems working also, and no need to write down the stats….

  9. Even if i can say that the disk is almost powerd on if you even access to the desktop, i dont see much usefull this can be, the drive goes on off too many times and this is harmfull for it.

    i just finished my netgear script today, nice stuff, keepd up the line at the max speed connection than can get on that moment and changes speed if the s/n or attenuation changes on the line to not break down,it’s a sort of stay on the max speed the line can support on that moment.
    running that for a few days now, and i believe who is far from the box it’s a need.

  10. Instead of /dev/sdX I’m using /dev/disk/by-uuid/$1 and address the drive by the uuid. That way it’s very easy to address a particular drive, as these a symlinks to the propper /dev/sdX. Udev does normally not guarantee that a certain drive is always mapped to the same /dev/sdX file but the uuid symlink does.

    Cheers

    Amie

  11. I added some checks and logging to the script…
    Now the spindown isn’t issued as long as the drive hasn’t spin up again.

    # !/bin/sh

    TMPF1=”/tmp/.hddspindwn-$1-1″
    TMPF2=”/tmp/.hddspindwn-$1-2″
    FLAG=”/tmp/.hddspindwnflg-$1″
    LOG_FILE=/var/log/hddspindown.log
    STAMP=`date +”%d.%m.%g %H:%M:%S”`

    [ ".$1" != "." ] && disks=$(ls /dev/sd* | grep $1)
    if [ ".$disks" = "." ]; then
    echo “Please enter a valid disk device” 1>&2
    exit 1;
    fi

    if [ "$(id -u)" != "0" ]; then
    echo “This script must be run as root” 1>&2
    exit 1
    fi

    # Get new state from diskstats
    nstats=$(cat /proc/diskstats | grep $1)
    echo $nstats > $TMPF1

    # compare md5 sums
    nmd5=”???”
    omd5=”???”
    [ -f $TMPF1 ] && nmd5=$(md5sum $TMPF1 | sed ‘s/ .*//’)
    [ -f $TMPF2 ] && omd5=$(md5sum $TMPF2 | sed ‘s/ .*//’)
    echo “$STAMP : Current MD5 : $nmd5″ >> $LOG_FILE
    echo “$STAMP : Old MD5 : $omd5″ >> $LOG_FILE

    # if no changes, power down
    if [ "$nmd5" = "$omd5" ]; then
    if [ ! -f $FLAG ]; then
    touch $FLAG
    hdparm -Y /dev/$1 &>/dev/null
    echo “$STAMP : Spindown of /dev/$1 intiated…” >> $LOG_FILE
    else
    echo “$STAMP : /dev/$1 was already set to standby.” >> $LOG_FILE
    fi
    else
    if [ -f $FLAG ]; then
    rm $FLAG
    echo “$STAMP : $FLAG removed because of disk-io.” >> $LOG_FILE
    fi
    fi

    # Write current state to file
    echo $nstats > $TMPF2

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>