#!/bin/bash
# Using /bin/bash and not /bin/sh to get a shell with support for random number
# generation.
#
# Automatically turn off computers at night, defined as 16:00 - 06:00.
# Should schedule wakeup in the morning.

# How do we decide if it should be enabled for this host?  Netgroup
# membership?  LDAP search?  Flag file?

#set -e

wakeuptime="07:00"

hostname="$(uname -n)"
netgroupname="shutdown-at-night-hosts"

# Hook directories to allow custom code to detect if a machine is used
# or not.
unuseddlist="/etc/shutdown-at-night/unused.d /usr/lib/shutdown-at-night/unused.d"

in_netgroup() {
    # Try both long and short name
    for h in "$(uname -n)" "$(hostname -s)" ; do
	if innetgr -h "$h" "$netgroupname" ; then
	    return 0
	fi
    done
    return 1
}

enabled_for_host() {
    # Flag for now
    if [ -f /etc/shutdown-at-night/shutdown-at-night ] || in_netgroup; then
        return 0
    fi
    return 1
}

# Take wakeup hour:minute in local time zone and return wake up time
# in seconds since EPOC UTC, radomized around the target wakeup time.
whentowakeup() {
    wakeuptime=$1
    # Spread the BIOS wakeup time across a 20 minute period, to avoid
    # all of them waking up at the same time, killing a fuse.

    # Convert local target time in seconds since EPOC UTC
    targettime=$(date +%s -d "tomorrow $wakeuptime $(date +%z)")
    echo $(( $targettime - 600 + $RANDOM % 1200))
}

nvramwakeup() {
# http://www.vdr-portal.de/board/thread.php?threadid=75582&sid=b7aced20e710aef12ffa56b5197fe168
    wakeuptime=$1
    when=$(whentowakeup $wakeuptime)

    # Make sure HW clock is correct, as it is used to decide when to
    # wake up.
    /etc/init.d/hwclock.sh stop > /dev/null

    # Make sure /dev/nvram is available
    modprobe nvram

    # This require a supported motherboard
    if nvram-wakeup -s $when > /dev/null ; then
        logger -t shutdown-at-night "scheduled $hostname to turn itself on at $wakeuptime using nvram-wakeup."
        return 0
    else
        return 1
    fi
}

# This method might not work if the hardware clock is updated during
# shutdown.  Changing /etc/default/hwclock to list HWCLOCKACCESS=yes
# to disable it.
acpiwakeup() {
    wakeuptime="$1"
    if [ -e /sys/class/rtc/rtc0/wakealarm ] ; then
	when=$(whentowakeup $wakeuptime)

        # First reset alarm
	echo 0 > /sys/class/rtc/rtc0/wakealarm

        # Next, set it to our selected time
	echo $when > /sys/class/rtc/rtc0/wakealarm
	logger -t shutdown-at-night "scheduled $hostname to turn itself on at $wakeuptime using /sys/class/rtc/rtc0/wakealarm."
	return 0
    else
	return 1
    fi
}

prepare_wakeonlan() {
    interface="$(/sbin/route -n | awk '/^0\.0\.0\.0 / { print $8 }')"
    ethtool -s $interface wol g
}

is_host_unused() {
    # Logged in users, or ldm connection to a remote server
    if [ "$(who)" ] ||  ps -efwww | egrep -q ' ssh .*LTSP_CLIEN[T]' ; then
	return 1
    fi
    # Uptime is less than one hour
    if (( $(cat /proc/uptime  | awk '{print int($1)}') < 3600 )) ; then
        return 1
    fi
    for dir in $unuseddlist ; do
	if [ -d $dir ] ; then
            for f in $dir/* ; do
		if [ -x $f ] && ! $f ; then
                    return 1
		fi
            done
	fi
    done
    return 0
}

fatal() {
    logger -t shutdown-at-night "$1"
    exit 1
}

if enabled_for_host ; then
    if is_host_unused; then
	logger -t shutdown-at-night "turning off unused client $hostname."
	if type nvram-wakeup >/dev/null 2>&1 ; then
	    nvramwakeup $wakeuptime
	fi
	acpiwakeup $waketime
	prepare_wakeonlan || fatal "unable to enable wake-on-lan - aborting shutdown."
	shutdown -h 5 "Unused host being turned off for the night (cron)." < /dev/null > /dev/null 2>&1 &
    else 
      logger -t shutdown-at-night "client $hostname is in use; shutdown sequence will NOT be initiated."
    fi
else
    logger -t shutdown-at-night "shutdown-at-night is not enabled for client $hostname."
fi

