#!/bin/bash

#########################################
# Name: xtrim
#
# Summary: trim log files
#
# Description:
#
# Author: k headley
#
# Copyright MBARI 2013
#
#########################################

#########################################
# Script configuration defaults
# casual users should not need to change
# anything below this section
##########################################
#CFG_PATH="${XF_HOME}/conf/xtrim.conf"
CFG_PATH=""

#################################
# Script variable initialization
#################################
VERBOSE="FALSE"
INDENT="  "
TEST="N"

if [ -d "${XF_HOME}/logs" ]
then
# set default working directory to XF_HOME/logs
WDIR=${XF_HOME}
else
# If XF_HOME/logs is invalid, set to cwd
WDIR=$PWD
fi

PERIOD=-1
#unset sleep_pid

#################################
# Function Definitions
#################################
#################################
# name: sigTrap
# description: signal trap callback
# will interrupt 
# args: none
#################################
sigTrap(){
    echo `date +%Y.%m.%d-%H:%M:%S`
	echo "CAUGHT Signal - exiting"
	#kill $sleep_pid
    exit 0;
}

#################################
# name: msg
# description: print message with indent
# args: none
#################################
msg(){
local MSG_IFS=$IFS
IFS=' '
echo "${INDENT}${*}"
IFS=$MSG_IFS
}

#################################
# name: printUsage
# description: print use message
# args: none
#################################
printUsage(){
    msg
    msg "usage: `basename $0` [options...]"
    msg "-f <cfg>           : config file    [$CFG_PATH]"
    msg "-d <cfg,size,segs> : definition"
    msg "-p <period_sec>    : set period     [$PERIOD]"
    msg "-t                 : test mode      [$TEST]"
    msg "-D                 : change dir     [$WDIR]"
    msg "-V                 : verbose output [$VERBOSE]"
    msg "-h                 : print help"
    msg ""
    msg
}


########################################
# name: vout
# description: print verbose message to stderr
# args:
#     msg: message
########################################
vout(){
    if [ "$VERBOSE" == "TRUE" ]
    then
	msg "$1" >&2
    fi
}

########################################
# name: exitError
# description: print use message to stderr
# args:
#     msg:        error message
#     returnCode: exit status to return
########################################
exitError(){
    msg >&2
    msg "$1" >&2
    msg >&2
    exit $2
}


########################################
# name: rotate
# description: rotate file
# args:
#     log : log file
#     segs: max segments
########################################
rotate(){
 local rot_file=${1}
 local max_seg=${2}
 msg "rot_file[${rot_file}] max_seg[${max_seg}]"
 if [ "${max_seg}" ] && [ "${max_seg}" -gt 0 ]	
 then
	# build list, sorted by ...?
	# in descending order
	# -rs
	old_logs=`ls -r $rot_file*.[0-9]*`
	for seg in $old_logs
	do
		suffix=${seg##*.}
		msg "seg[$seg] suf[$suffix]"
		if [ "${suffix}" -ge 0 ] && [ "${suffix}" -lt "${max_seg}" ]
		then
		  let "max_n=${max_seg}-1"
		  if [ "${suffix}" -eq "${max_n}" ]
		  then
		    msg "emptying segment[${seg}]"
			if	[ "${TEST}" == "N" ]
			then
			   > ${seg}
			else
			   msg "TEST: > ${seg}"
			fi
		  else
		    let "new_suf=${suffix}+1"
			printf -v new_seg "%s.%04d" "${rot_file}" "${new_suf}"
   		    msg "rotating segment[${seg}] to [${new_seg}]"
			if	[ "${TEST}" == "N" ]
			then
			  mv ${seg} ${new_seg}
			  # or
			  #dd if=${seg} of=${new_seg} bs=...skip=...
			else
			  msg "TEST: mv ${seg} ${new_seg}"
			fi
		  fi
		else
		  msg "seg $seg not in range - ignoring"    
		fi 
	done
	
	new_suf=0;
	printf -v new_seg "%s.%04d" "${rot_file}" "${new_suf}"
	
	msg "rotating current log[${rot_file}] to [${new_seg}]"
	if	[ "${TEST}" == "N" ]
	then
		dd if=${rot_file} of=${new_seg}
	else
		msg "TEST: dd if=${rot_file} of=${new_seg}"
	fi
	
  else	
	msg "max_seg=0 no rotation for [${rot_file}]"
 fi
  msg
}


########################################
# name: trim
# description: trim file
# args:
#     log : log file
#     size: max size
#     segs: max segments
########################################
trim(){
 local fspec=${1}
 local trim_sz=${2}
 local trim_segs=${3}
 set +f
 fspec_exp=${1}
 flist=`ls $fspec_exp`
 msg "fspec[${fspec}] trimsz[${trim_sz}] trimsg[${trim_segs}]"
 msg "flist[$flist]"

	for trim_log in $flist
	do

 msg "log[$trim_log]"
 msg "trim_sz[$trim_sz] trim_segs[$trim_segs]"
	
 let "tlen=${#trim_sz}"
 let "tslen=${tlen}-1"
 blocksz=${trim_sz:${tslen}:1}
 count=0;
 bytes=0xFFFFFFFFFFFFFFFF;
 #msg tlen[$tlen] tslen[$tslen]
 case ${blocksz} in 
	K )
	let "count=${trim_sz:0:${tslen}}"
	let "bytes=${count}*1024"
	;;
	M )
	let "count=${trim_sz:0:${tslen}}"
	let "bytes=${count}*1024*1024"
	;;
	G )
	let "count=${trim_sz:0:${tslen}}"
	let "bytes=${count}*1024*1024*1024"
	;;
	* )
	let "count=${trim_sz:0:${tlen}}"
	let "bytes=${count}"
	blocksz="B"
	;;
 esac
 
 msg "count[${count}] blocksz[${blocksz}] bytes[${bytes}]"

 if [ -f "${trim_log}" ] && [ -w "${trim_log}" ]
 then
	#msg "checking [${trim_log}]"
	log_bytes=`du -b ${trim_log}|sed -e "s/\([0-9]*\)\( *.*\)/\1/"`
	if [ "$log_bytes" -gt "$bytes" ]
	then
		msg "file $trim_log size $log_bytes is ready"
		rotate ${trim_log} ${trim_segs}
		msg "truncating log[${trim_log}]"
		if	[ "${TEST}" == "N" ]
		then
			>${trim_log}
		else
			msg "TEST: >${trim_log}"
		fi
	else
		msg "file $trim_log size $log_bytes not ready"
	fi
 else
	msg "file $trim_log is wrong type, not writable"
 fi

 echo
 done
 return

}

##########################
# Script main entry point
##########################

# Argument processing
if [ "$#" -eq 0 ];then
    printUsage
   # exit 0
fi

declare -a DEFS
def_count=0
while getopts p:f:d:D:thV Option
do
    case $Option in
	p ) PERIOD="$OPTARG"
	;;
	f ) CFG_PATH="$OPTARG"
	;;
	d ) vout "d = $OPTARG"
	    DEFS[$def_count]="${OPTARG}"
		let "def_count+=1"
	;;
	t ) TEST="Y"
	;;
	D ) WDIR="${OPTARG}"
	;;
	V ) VERBOSE="TRUE"
	;;
	h)printUsage
	  exit 0
	;;
	*) exit 0 # getopts outputs error message
	;;
    esac
done


# call sigTrap on INT,TERM or EXIT
trap sigTrap INT HUP TERM EXIT

# reset trapped signals
#trap - INT TERM EXIT

cd ${WDIR}
cdok=$?
if [ "$cdok" -ne 0 ]
then
  exitError "Change directory [$WDIR] failed - exiting" 1
fi

noconf="Y"

while [ 1 ]
do
# process config file definitions, if any
if [ "${CFG_PATH}" ] && [ -f "${CFG_PATH}" ] && [ -s "${CFG_PATH}" ]
then
  noconf="N"
  OLD_IFS=$IFS
	while IFS="," read log size segs
	do
		#msg "log[$log] sz[$size] n[$segs]"
		set -f
		trim $log $size $segs
	done < $CFG_PATH
  IFS=$OLD_IFS

fi
 
# process command line definitions, if any
if [ "${#DEFS}" -gt 0 ]
then
  noconf="N"
  msg "processing defs ${DEFS[@]}"
  OLD_IFS=$IFS
  idx=0
  for def in ${DEFS[*]}
	do
	 IFS=","
	 read log size segs <<< "${def}"
	 #msg "log[$log] sz[$size] n[$segs]"
 	 set -f
	 IFS=$OLD_IFS
	 trim $log $size $segs
	done
  IFS=$OLD_IFS
fi

if [ "$noconf" == "Y" ]
then
 exitError "configuration unspecified or invalid [$CFG_PATH]" 1   
fi	

if [ "${PERIOD}" -gt 0 ]
then
#unset sleep_pid
#sleep $PERIOD & sleep_pid=$!; wait
sleep $PERIOD
else
break
fi

done

# return success
exit 0