#!/bin/bash # Copyright (C) 2008-2015 Petter Reinholdtsen # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA # 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" NVRAM_WAKE_ENABLED="no" ACPI_WAKE_ENABLED="no" if [ -r /etc/default/shutdown-at-night ]; then . /etc/default/shutdown-at-night fi wakeuptime="$WAKEUPTIME" hostname="$(uname -n)" netgroupname_wakeup="shutdown-at-night-wakeup-hosts" netgroupname_nowakeup="shutdown-at-night-wakeup-hosts-blacklist" netgroupname_shutdown="shutdown-at-night-hosts" netgroupname_noshutdown="shutdown-at-night-hosts-blacklist" netgroup_wakeup_found=$(netgroup -h $netgroupname_wakeup 1>/dev/null 2>/dev/null && printf "FOUND" || printf "NOT-FOUND"); # 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_nowakeup_netgroup() { # Try both long and short name for h in "$(uname -n)" "$(hostname -s)" ; do if innetgr -h "$h" "$netgroupname_nowakeup" ; then return 0 fi done return 1 } in_wakeup_netgroup() { # Try both long and short name for h in "$(uname -n)" "$(hostname -s)" ; do if innetgr -h "$h" "$netgroupname_wakeup" ; then return 0 fi done return 1 } in_shutdown_netgroup() { # Try both long and short name for h in "$(uname -n)" "$(hostname -s)" ; do if innetgr -h "$h" "$netgroupname_noshutdown"; then return 1 elif innetgr -h "$h" "$netgroupname_shutdown" ; then return 0 fi done return 1 } wakeup_enabled_for_host() { # Flag for now if [ -f /etc/shutdown-at-night/shutdown-at-night-nowakeup-self ]; then logger -t shutdown-at-night "Blocking NVRAM/RTC based wake-up of client $hostname (myself); remove /etc/shutdown-at-night/shutdown-at-night-nowakeup-self if wanted otherwise." return 1 elif [ -f /etc/shutdown-at-night/shutdown-at-night ]; then return 0 elif in_nowakeup_netgroup; then return 1 elif in_wakeup_netgroup; then return 0 elif [ "x$netgroup_wakeup_found" != "xFOUND" ] && (! in_nowakeup_netgroup ) && in_shutdown_netgroup; then return 0 fi return 1 } shutdown_enabled_for_host() { # Flag for now if [ -f /etc/shutdown-at-night/shutdown-at-night ] || in_shutdown_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. # Based on 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 2>/dev/null } # Return true if local user is logged in, false otherwise is_local_user() { if [ "$(who | grep -v '\(unknown\)')" ] ; then return 0 else return 1 fi } # Return true if X2Go thin client login is active is_active_x2go_session() { if ps aux | grep sshd: | grep @notty ; then return 0 else return 1 fi } # Return false if X session is confirmed unused (ie login screen is # shown). If not sure, claim it is used to be safe. is_xsession_used() { for s in \ /var/run/gdm3/auth-for-Debian-gdm-*/database \ /var/lib/lightdm/.Xauthority \ /var/run/xauth/* \ /run/xauth/*; do if [ -e "$s" ] ; then if XAUTHORITY="$s" DISPLAY=:0 xlsclients | egrep -q 'kdmgreet|lightdm-gtk-greeter|razor-lightdm-greeter|lightdm-kde-greeter|arctica-greeter' \ || [ -d /var/run/gdm3/greeter ] ; then return 1 fi fi done return 0 } is_host_unused() { # Logged in users, or X2Go connection to a remote server if is_xsession_used || is_local_user || is_active_x2go_session; 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 shutdown_enabled_for_host ; then if is_host_unused; then logger -t shutdown-at-night "turning off unused client $hostname." if wakeup_enabled_for_host; then logger -t shutdown-at-night "Preparing client $hostname for waking up in the morning." if [ type nvram-wakeup >/dev/null 2>&1 ] && [ "$NVRAM_WAKE_ENABLED" = yes ] ; then nvramwakeup $wakeuptime fi if [ "$ACPI_WAKE_ENABLED" = yes ] ; then acpiwakeup $wakeuptime fi prepare_wakeonlan || fatal "unable to enable wake-on-lan - aborting shutdown." fi 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