#!/bin/bash
# Notes:
# the original source can be found
# https://linuxwiki.de/hdparm#disk_spindown.sh
# I felt free to do some minor changes
# - delay is performed before the first check, to avoid unnecessary standby procedure after boot
# - not existent drives are skipped
# - output to logfile /var/log/diskspindown.log
# - ramdrive changed to /var/tmp
# - optional parameter: spindowntime may be overwritten with param 1 (default: 2100)
# - optional parameter: new maxcount may be set with param 2 (default: 0, respective infinit)
# - contact: nikolausdulgeridis at gmail.com
# Purpose of this script:
# Check for disk data transfer and spin them down after approx. 20 min
# explanation: often harddrive firmware fails to spindwon with hdparm -B oder -S settings.
# but the hdparm -y command works directly with (nearly) every disk.
# So that is a script for monitoring that harddrives.and spin them down after a given time
# documentation: http://linuxwiki.de/hdparm
#
# Installation:
# ---------------
# For this script hdparm must be installed, but not configured!
# Only a apm=255 is maybe needed in the hdparm.conf for the Drive.
#
# - Put it to /usr/local/bin/disk_spindown.sh
# - Set the Permissons sudo chmod 744 /usr/local/bin/disk_spindown.sh
# - Startup: we put a new line in /etc/rc.local for systemwide start.
# /usr/local/bin/disk_spindown.sh & >/dev/null
# exit 0
#
# Now you can reboot the pc, for activate or to activate a change.
#
# #
# # *** some information for older disks ***
# #
# # Western Digital Green Harddrives (mostly not suitable with hdparm -S or -B)
# # ---------------------------------------------------------------------------
# # Modern Western Digital "Green" Drives include the Intellipark feature that stops the disk when not in use.
# # Unfortunately, the default timer setting is not perfect on linux/unix systems, including many NAS,
# # and leads to a dramatic increase of the Load Cycle Count value (SMART attribute #193).
# # Please deactivat it with http://idle3-tools.sourceforge.net/ (normally in the Distro)
#
# # get the Info: idle3ctl -g /dev/sd(x)
# # disabling : idle3ctl -d /dev/sd(x)
# # The idle3 timer seems to be a power on only setting.
# # That means that the drive needs to be powered OFF and then ON to use the new setting.
# # I turn off the Computer, plug the power off and push the start button, for a short while the fan go's on.
# # All power it out.
# # Or turn off for few minutes to take the effect.
# # **************************************************
##################################################
### troubleshooting ###
# please check your PATH settings if you run into problems, also make sure your script is executing with root permissions
# especially when using crontab, it may happen, that the path settings differ from what you expect.
# so you should test under real conditions, to be sure its working
# typical paths
# which diff
# /usr/bin/diff
# which hdparm
# /sbin/hdparm
# which cat
# /bin/cat
### logfile watching
# watch "cat /var/log/disk_spindown.log && echo --- && ps -ef | grep spindown | grep -vw grep"
##################################################
### Logfile:
diskspindownlog="/var/log/disk_spindown.log"
# Where is your Ramdrive?
# Check it out with df -h or mount command.
# Possible Values Examples: /dev/shm , /run/shm
#ramdrive=/run/shm
ramdrive=/var/tmp
# The Spindwon Time in Seconds
# The Raktion done in $spindowntime maximum of 2 times. Depending build by the script. Protects for the cpu load.
spindowntime=2100;
if [ $1 ] ; then let spindowntime="$1"; fi
# Counter
# counter, 0 means infinite
let counter=0;
if [ $2 ] ; then let counter="$2"; fi
# Log
# appendlog= append log, newlog=new log (default), nolog= no output to logfile
logmode="newlog";
if [ $3 ] ; then logmode="$3"; fi
if [ "$logmode" == "nolog" ]; then diskspindownlog="/dev/null"; fi #!!caution!! stringcompare spaces: if_[_$1_==_$2_]; then usw.
if [ "$logmode" == "newlog" ]; then rm $diskspindownlog; fi
# Wich disks are handled
diskvolumes="a b c d e f g h i j k l m n o p q r s t u v w x y z"
#watch "date && cat /proc/diskstats | grep sd"
# Build onece the tempfiles
if [ ! -f $ramdrive/diskstate1 ]; then touch $ramdrive/diskstate1;fi
if [ ! -f $ramdrive/diskstate2 ]; then touch $ramdrive/diskstate2;fi
###
#Wait before 1st run and initialize file diskstate1
#Start logging
echo '***'
echo start disk monitoring: disk_spindown.sh - harddrive standby after inactivity
echo start disk monitoring: disk_spindown.sh - harddrive standby after inactivity >> $diskspindownlog
echo "date: $(date) --- settings: "seconds:$spindowntime repeat:$counter "
echo "date: $(date) --- settings: "seconds:$spindowntime repeat:$counter " >> $diskspindownlog
echo '************************************************************************ '
echo '* usage: [/bin/bash] disk_spindown.sh [ <delay> [ <count> [ [newlog|appendlog|nolog] ] ] ] '
echo '* example: /usr/local/bin/disk_spindown.sh 600 0 >/dev/null & # stby after approx.10 min, infinite'
echo "* view log: cat /var/log/disk_spindown.log"
echo '************************************************************************ '
echo '*** ps -ef | grep spindown | grep -vw grep ***'
ps -ef | grep spindown | grep -vw grep
echo '*** ps -ef | grep spindown | grep -vw grep ***' >> $diskspindownlog
ps -ef | grep spindown | grep -vw grep >> $diskspindownlog
echo '****************************************************************'
cat /proc/diskstats > $ramdrive/diskstate1
sleep $spindowntime
# Here we go...
let count=0;
let shutdown=0;
while [ true ] ; do
echo '('"$count"')' $(date)
echo '('"$count"')' $(date) >> $diskspindownlog
# cycle it to test for activity
mv $ramdrive/diskstate1 $ramdrive/diskstate2
cat /proc/diskstats > $ramdrive/diskstate1
# Loop through all array disks and spin down idle disks.
let allidle=1;
for disk in $diskvolumes
do
if [ "$(diff $ramdrive/diskstate1 $ramdrive/diskstate2 | grep -w sd$disk )" = "" ] ; then
# skip drive letters that are currently not used
if [ $(cat $ramdrive/diskstate1 | grep -cw -m 1 sd$disk ) != "0" ] ; then
sudo hdparm -y /dev/sd$disk >/dev/null 2>&1;
#echo hdparm -y /dev/sd$disk
#echo hdparm -C /dev/"sd$disk"
# removed: echo $(hdparm -C /dev/"sd$disk" 2> /dev/null | grep "sd$disk" -A 1)
# removed: echo $(hdparm -C /dev/"sd$disk" 2> /dev/null| grep "sd$disk" -A 1) >> $diskspindownlog
echo spindown "sd$disk"
echo spindown "sd$disk" >> $diskspindownlog
fi;
else
let allidle=0;
fi;
done
let count+=1; if [ "$counter" -eq "$count" ]; then break; fi
if [ $((count % 999)) -eq 0 ]; then rm $diskspindownlog; echo remove; fi # simple logfile size limit
if [ $allidle -eq 1 ] ; then
let allidlecount=$allidlecount+1;
else
let allidlecount=0;
fi;
if [ $allidle -eq 1 ] ; then
sudo fstrim -av
fi;
#echo allidle $allidle; echo allidle $allidle >> $diskspindownlog
#echo allidlecount $allidlecount; echo allidlecount $allidlecount >> $diskspindownlog
##check networkactivity 1 sec
#nwstat=($(dstat -n --bw 1 1 | grep -v e)); let nwaktiv=0; for ele in "${nwstat[@]}"; do if [ $ele != "0" ]; then let nwaktiv+=1 ;fi ; done ;
#for ele in "${nwstat[@]}"; do echo $i $ele; let i+=1; done
#echo nwaktiv $nwaktiv
#if [ $nwaktiv -ge 1 ] ; then
# allidlecount=0;
#fi
#Wait
sleep $spindowntime
done
exit 0
#changes 11.1.2020
# removed status check hdparm -C and replaced with following lines, reason is some drives wake up when receiving status quests
# # removed: echo $(hdparm -C /dev/"sd$disk" 2> /dev/null | grep "sd$disk" -A 1)
# # removed: echo $(hdparm -C /dev/"sd$disk" 2> /dev/null| grep "sd$disk" -A 1) >> $diskspindownlog
# echo spindown "sd$disk"
# echo spindown "sd$disk" >> $diskspindownlog
# remarks:
# - allidle, allidlecount: not used, the idea was to detect inactivity and power down after some time
# - i tested also with -Y (sleep) instead of -y (spindown), but with seagate i got n advantage, quite the contrary
# same power consumption but long spinup time (5 seconds)
#changes 2.2.2020
#added trim command for ssds
#if [ $allidle -eq 1 ] ; then
# sudo fstrim -av
#fi;
#changes 8.2.2020
#line added:
#if [ $((count % 999)) -eq 0 ]; then rm $diskspindownlog; echo remove; fi # simple logfile size limit