As promised, a much better cron script for putting the computer to sleep. It’s actually two scripts – I split the hibernating functionality away from the “is idle” functionality. The hibernate script is simple and just includes my final result from this post. I stored this as /root/hibernate.

#!/bin/sh
#
# Hibernate this machine for WOL with unicast and magic packets.
# 2009 Nathan Blythe
#

ethtool -s eth0 wol ug
pm-hibernate

The script that does the idle checking is stored to /root/useridle:

#!/bin/sh
#
# Execute command if system is "user-idle"
# 2009 Nathan Blythe
#
# Run without arguments for usage.
#

# Filepath of temporary file used by this script.
#
FIL=/tmp/useridle.tmp


# Get the command line arguments.
#
idl=$1
cmd=$2


# Invalid arguments.
#
if [ "$idl" != "reset" ] && [ -z "$cmd" ]; then
  echo "Usage:"
  echo "  useridle [reset]"
  echo "    Reset the system idle time."
  echo
  echo "  useridle  "
  echo "    If the system has been userless for idl seconds, execute"
  echo "    cmd and reset."
  echo
  exit
fi


# Determine the current time.
#
newtime=$(date +%s)


# Reset.
#
# Write the current time to the temporary file.
#
if [ "$idl" = "reset" ]; then
    echo $newtime > $FIL
# Ordinary operation.
#
else
  # If the temporary file does not exist or at least one user is
  # logged in, write the current time to the temporary file.
  #
  if [ ! -e $FIL ] || [ "$(users)" ]; then
    echo $newtime > $FIL


  # Otherwise if it's been at least idl seconds since the time in the
  # temporary file, execute cmd and then reset the temporary file.
  #
  else
    oldtime=$(( $(cat $FIL) + $idl ))

    if [ $newtime -ge $oldtime ]; then
      $cmd
      echo $newtime > $FIL
    fi
  fi
fi

This is a simple script (most of that is just fuzz) that keeps track of the time at which users were seen on the system. When you run it, it checks to see if there are users logged in; if so, it stores the current time to a file. If there are no users logged in, it checks the file to see when was the last time users were logged in. If the desired amount of time has gone by, the provided command is executed.

I use this script by adding the following lines to /etc/crontab.

@reboot         root    /root/useridle reset
*  *    * * *   root    /root/useridle 180 /root/hibernate

This runs my script with the “reset” argument when the computer starts up – this is to make sure it doesn’t hibernate immediately, since the file might have the time from before the machine was shut down. Every 60 seconds it also runs the script with the arguments “180” and my hibernate script. Thus every 60 seconds the machine checks if there haven’t been any users logged on in the past 3 minutes. If this is the case, it hibernates until it is woken by a remote login.

All that needs to be done to configure the script is to edit /etc/crontable. There I can change the first “*” on the second line to, for instance, “*/5” or “*/15” to have the script run every 5 or 15 minutes, respectively, and adjust the “180” argument. So long as the idl parameter (180 seconds, currently) is significantly longer than the period of the cron job (60 seconds, currently) it will be somewhat accurate.

Pretty nifty, huh? I haven’t solved the problem of SSH only waking the machine (on the first try) and having to try again to log in. I think that might just not be doable without changing how SSH attempts to connect. I may forward another port to the server so I can use some common command to boot the system. Either way, I’m happy with how it works.

I will post a summary of all this information at some point in case anybody ever wants a similar set up. It sure is power efficient, especially for doing things like a remote SSH file transfer for backups. I may forward rsync to the machine too… a backup server that (more or less) transparently boots up when needed and shuts down when finished… very nifty!

Advertisement