#!/bin/bash

# script to prune old backups, based on full backups made every Friday and the last day of the month
# written by Gerald Brandt

# @todo add code for skip array (skipping manual deletion on systems)
#       currently hardcoded on line(s) 210+

topdir="/var/lib/backuppc"
hostsfile="/etc/backuppc/hosts"
friday="Fri"
december="Dec"
#pretend="1"        # if this is '1' then we'll only pretend to prune
pretend="0"        # if this is '1' then we'll only pretend to prune
deletenonconforming="1"   # if '1' then delete any backups that don't conform to Fridays, end of month, end of year
#deletenonconforming="0"   # if '1' then delete any backups that don't conform to Fridays, end of month, end of year
keepmonthly=25    # keep slightly more months than in a year
keepyearly=7
keepweekly=10     # keep slightly more weeks than in two months
cutoffyear=2013   # anything older than month/year, we don't touch

email="itservices@erlphase.com"  

# these backups are not rotated
declare -a skipsys=("c-00000001" "c-00000015" "c-00000071" "jeff1500.norscan.com")

set -e

usage()
{
  cat <<EOF
Usage: `basename $0` 

Delete old full backups, and full backups that don't land on our scheduled dates.

Ties in to having full backups made on Fridays, and the last day of the month.

EOF
  exit 1
}

main()
{
  hosts=""

  if [ -f /tmp/backup.list ]
  then
    rm /tmp/backup.list
  fi
  touch /tmp/backup.list

  if [ -f /tmp/backup.date ]
  then
    rm /tmp/backup.date
  fi
  touch /tmp/backup.date

  if [ -f /tmp/backup.delete ]
  then
    rm /tmp/backup.delete
  fi
  touch /tmp/backup.delete


  # get all hosts in the host file 
  for HOST in $(cat $hostsfile | grep ^[A-Za-z0-9] | grep -v host | grep -v archive | awk '{print $1}')
  do
    print_stats $HOST
  done

  # go through the known backups file for all hosts
  while read line; 
  do
    parse_date $line
  done < /tmp/backup.list

  # get all the Fridays into one file, and the non Fridays into another
  cat /tmp/backup.date | grep -v "$friday" > /tmp/backup.notfri
  cat /tmp/backup.date | grep "$friday" > /tmp/backup.valid

  # go through the non fridays and look for the last day of the month
  while read line; 
  do
    parse_lastday $line
  done < /tmp/backup.notfri

  
  # go through the delete file, and delete the backups
  echo "Deleting invalid date backups..."
  if [ $deletenonconforming -eq "1" ]
  then
    if [ -f /tmp/mail.delete ]
    then
      rm /tmp/mail.delete
    fi

    echo "subject: BackupPC Weekly Pruning" > /tmp/mail.delete

    if [ $pretend -eq "1" ]
    then
      echo "(Pretend Mode) These are the delete commands I would have run:" >> /tmp/mail.delete
      echo " " >> /tmp/mail.delete
    fi

    echo "Deleting invalid date backups..."  >> /tmp/mail.delete
    echo " " >> /tmp/mail.delete

    while read line
    do
      delete_backup $line
    done < /tmp/backup.delete
  fi
  
  # go through the valid file and delete anything too old
  echo "Deleting expired backups..."
  echo " " >> /tmp/mail.delete
  echo "Deleting expired backups..." >> /tmp/mail.delete
  echo " " >> /tmp/mail.delete

  while read line
  do
    age_backup $line
  done < /tmp/backup.valid
  
  echo "Done." >> /tmp/mail.delete  

  /usr/lib/sendmail -i $email < /tmp/mail.delete  
  
}

# get all the backups known for the passed in host and save in file /tmp/backup.list
print_stats()
{
  local host="$1"

  echo HOST: $host  

  grep "full" "$topdir/pc/$host/backups" | \
  
  awk -v host=$host 'BEGIN { }
         {printf "%d %d %s\n", $1, $3, host}' >> /tmp/backup.list
 
}

# convert the date in seconds to date text and save in file /tmp/backup.date
parse_date()
{
  local number=$1
  local secs=$2
  local host=$3

  date=$(perl -e "print scalar(localtime($secs))")

  # get the components I want
  day=$(echo $date | awk '{print $1}')
  month=$(echo $date | awk '{print $2}')
  year=$(echo $date | awk '{print $5}')
  num=$(echo $date | awk '{print $3}')
  
  echo $day $num $month $year $number $host $secs >> /tmp/backup.date
}


# if a day is the last day of the month, attach it to the valid file other it goes into the delete file
parse_lastday()
{
   local day=$1
   local num=$2
   local month=$3
   local year=$4
   local number=$5
   local host=$6
   local sec=$7

   # get the last work day of the month
#   last=$(cal $month $year | awk '{ print $2, $3, $4, $5, $6 }' | tr -s '[:blank:]' '\n' | tail -n1)

   # get the last day of the month
   last=$(cal $month $year | tr -s '[:blank:]' '\n' | tail -n1)

   # if the date is the last workday of the month, save it as well   
echo last workday: $last  $num
   if [ $last -eq $num ]
   then
     echo $day $num $month $year $number $host $sec >> /tmp/backup.valid
   else
     echo $day $num $month $year $number $host $sec >> /tmp/backup.delete
   fi
   
}

# delete the backup passed in
delete_backup()
{
   local day=$1
   local num=$2
   local month=$3
   local year=$4
   local number=$5
   local host=$6
   local secs=$7
   local DIRECTORY="/var/lib/backuppc/pc/$host/$number"
   
   # special case anything in 'cutoffyear' or older is skipped, since we put this backup planinto place in 2010
   if [ $year -gt $cutoffyear ]
   then
     if [ $pretend -eq "1" ]
     then
       case " ${skipsys[*]} " in
         *\ $host\ *)
           echo "skipping $host" >> /tmp/mail.delete
           ;;
         *)
           echo "/usr/share/backuppc/bin/BackupPC_manualDelete -c $host -d $number  DATE: $day  $year-$month-$num" >> /tmp/mail.delete
            ;;
       esac

     else
       case " ${skipsys[*]} " in
         *\ $host\ *)
           echo "skipping $host" 
           ;;
         *)
           echo "/usr/share/backuppc/bin/BackupPC_manualDelete -c $host -d $number  DATE: $day  $year-$month-$num" >> /tmp/mail.delete
           echo "Deleting:  $host  $number  DATE: $day  $year-$month-$num" 
           if [ ! -d "$DIRECTORY" ]; then
              # Control will enter here if $DIRECTORY doesn't exist
              echo "    Directory $DIRECTORY doesn't exist, creating it"
              mkdir $DIRECTORY
           fi
           /usr/share/backuppc/bin/BackupPC_manualDelete -c $host -d $number
           echo
            ;;
       esac
       
#       if [ "$host" == "c-00000001" ]
#       then
#          echo "Skipping c-00000001"
#       else
#          echo "/usr/share/backuppc/bin/BackupPC_manualDelete -c $host -d $number  DATE: $day  $year-$month-$num" >> /tmp/mail.delete
#          echo "Deleting:  $host  $number  DATE: $day  $year-$month-$num" 
#          if [ ! -d "$DIRECTORY" ]; then
#             # Control will enter here if $DIRECTORY doesn't exist
#             echo "    Directory $DIRECTORY doesn't exist, creating it"
#             mkdir $DIRECTORY
#          fi
#          /usr/share/backuppc/bin/BackupPC_manualDelete -c $host -d $number
#          echo
#       fi
     fi
   fi
}


age_backup()
{
   local day=$1
   local num=$2
   local month=$3
   local year=$4
   local number=$5
   local host=$6
   local secs=$7
   
   # get the current date in seconds since 1970, and separate (date format: Sun Jan 31 13:09:54 CST 2010)
   local now=$(date +"%s")
   local nowday=$(date | awk '{print $1}')
   local nowmonth=$(date | awk '{print $2}')
   local nowyear=$(date | awk '{print $6}')

   local oneday=86400
   local oneweek=604800

   # expire the weeklys
   temp=$(($oneweek * $keepweekly))
   weekkeep=$(($now - $temp))   
   if [ $secs -lt $weekkeep ]  # if the timestamp is more than $keepweekly weeks away from current
   then
     # the backup is past our week count, is it a month end

     # get the last day of the month
     last=$(cal $month $year | tr -s '[:blank:]' '\n' | tail -n1)
     
     if [ $num -ne $last ]      # so, we are not the last day of a month
     then
       delete_backup $day $num $month $year $number $host $secs
       echo Week: $day  $year-$month-$num  SECS:$secs  KEEP:$weekkeep  NOW:$now   $temp
       return
     fi
   fi  

   # expire the monthly (we don't do December, yearly takes care of that)
   echo Checking Month...
   if [ $month != $december ]
   then
     delmonth=0
     delyear=$year
     monthname=$month
     monthnum=0
     $(monthtonum)   # convert month to number
     echo Month Number: $monthnum
     temp=$(($monthnum - $keepmonthly))
echo delmonth temp $temp

     delmonth=$temp
     while [ $delmonth -lt 1 ]
     do
       delmonth=$((12 + $delmonth))     # get the right month
       delyear=$(($delyear - 1))    # and go to the previous year
     done
     
     # get last day of delete month
     last=$(cal $delmonth $delyear | tr -s '[:blank:]' '\n' | tail -n1)
     delsecs=$(date --date $delyear-$delmonth-$last +"%s")   # convert date to seconds since 1970
     
     # if the timestamp is less than our delete date
     if [ $secs -lt $delsecs ]
     then
       delete_backup $day $num $month $year $number $host $secs
#       echo Month: $day  $year-$month-$num  SECS:$secs  KEEP:$weekkeep  NOW:$now   $temp
       return
     fi
   fi
   
   # we'll expire the years
   # if the year isn't our currrent year, and month isn't December, then delete
   echo Checking Year...
   if [ $year -lt $nowyear ]   # is the passed in year less than the current year?
   then
     if [ $month != $december ]  # is the month not December
     then
#       delete_backup $day $num $month $year $number $host $secs
#       echo Year: $day  $year-$month-$num
       return
     else   # month IS December
       temp=$(($nowyear - $keepyearly))   # claculate cut off year
       if [ $year -lt $temp ]     # year is past our cutoff
       then
         delete_backup $day $num $month $year $number $host $secs
       fi
     fi
   fi
   
   # get the last work day of the month
#   last=$(cal $month $year | awk '{ print $2, $3, $4, $5, $6 }' | tr -s '[:blank:]' '\n' | tail -n1)

   # if the date is the last workday of the month, check it's age
#   if [ $last -eq $num ]
#   then
#      diff=( $now - $secs )
#      echo Monthly delete: $diff
#   else
#      diff=( $now - $secs )
#      echo Weekly delete: $diff
#   fi
   
}


# This function expects English Style months
monthtonum()
{
  if [ $month = "Jan" ]
  then
    monthnum=1
  fi
  
  if [ $month = "Feb" ]
  then
    monthnum=2
  fi
  
  if [ $month = "Mar" ]
  then
    monthnum=3
  fi
  
  if [ $month = "Apr" ]
  then
    monthnum=4
  fi
  
  if [ $month = "May" ]
  then
    monthnum=5
  fi
  
  if [ $month = "Jun" ]
  then
    monthnum=6		
  fi
  
  if [ $month = "Jul" ]
  then
    monthnum=7
  fi
  
  if [ $month = "Aug" ]
  then
    monthnum=8
  fi
  
  if [ $month = "Sep" ]
  then
    monthnum=9
  fi
  
  if [ $month = "Oct" ]
  then
    monthnum=10
  fi
  
  if [ $month = "Nov" ]
  then
    monthnum=11
  fi
  
  if [ $month = "Dec" ]
  then
    monthnum=12
  fi

  echo monthtonum $monthnum
}

main "$@"

