#!/bin/bash
#
# retrieve.sh v1.0 2006/04
#
# Shell script for retrieving a group of fileS from multiple via scp
# remote hosts via secure copy (scp).
#
# Copyright (C) 2006 by Pedro Venda < pjvenda (at) pjvenda org >
#
# Distributed under the terms of the GNU Public License Agreement (GPLv2)
# http://www.fsf.org/licensing/licenses/gpl.html or
# http://www.fsf.org/licensing/licenses/gpl.txt
#

#
# Usage
# =====
#
# Setup configuration files:
#   rem_files.conf (file1
#		    file2)
#   hosts.conf (host1
#               host2)
#   local_dest.conf (local_path)
# Run script
#   ./retrieve.sh
#   (best used with ssh keys, of course...)
#
# Changelog
# =========
#
# v1.0 2006/04/17
# - initial version
#

# relevant binaries
CAT=/bin/cat
SSH=/usr/bin/ssh
SCP=/usr/bin/scp
CUT=/bin/cut
EGREP=/bin/egrep
RM=/bin/rm
DATE=/bin/date
MKDIR=/usr/bin/mkdir

# directories
BASE_DIR=.
CONF_DIR=${BASE_DIR}/conf
LOG_DIR=${BASE_DIR}/log

# configuration files
FILES_CONF=${CONF_DIR}/rem_files.conf
HOSTS_CONF=${CONF_DIR}/hosts.conf
DEST_CONF=${CONF_DIR}/local_dest.conf

# remote username
USERNAME=root

# logfile (filename or '-' for stdout)
#LOG_FILE=${LOG_DIR}/retrieve.log
LOG_FILE=-

# ignore comments on configuration files with this REGEXP
LINE_IGNORE="(^#.*|^ *$|^$)"

SL=">>>>>>>> --"
SR="-- <<<<<<<<"

#
# calculates current time
# do_time generates time string like hh:mm:ss on $TIME
# do_date generates date string like yyyy-mm-dd on $TODAY
# do_now generates time string like yyyy-mm-dd hh:mm:ss on $NOW
#
# usage: do_time
# usage: do_date
# usage: do_now
function do_time() {
        TIME=$(${DATE} +"%T")
}
function do_date() {
        TODAY=$(${DATE} +"%F")
}
function do_now() {
        do_time
        do_date
        NOW="${TODAY} ${TIME}"
}

#
# check for files and config directories existence and permissions
#
# usage: check_dirs
#
function check_dirs() {
	echo -n "checking for necessary directories... "
	if [ ! -d ${BASE_DIR} ] || [ ! -x ${BASE_DIR} ]; then
		echo "unable to find or read ${BASE_DIR}"
		return 0
	fi
	if [ ! -d ${CONF_DIR} ] || [ ! -x ${CONF_DIR} ]; then
		echo "unable to find or read ${CONF_DIR}"
		return 0
	fi
	echo "ok"
	return 1
}

#
# check for configuration files existence and readability
#
# usage: check_config_files
#
function check_config_files() {
	echo -n "checking configuration files... "
	if [ ! -r ${FILES_CONF} ]; then
		echo "unable to find or read ${FILES_CONF}"
		return 0
	fi
	if [ ! -r ${HOSTS_CONF} ]; then
		echo "unable to find or read ${HOSTS_CONF}"
		return 0
	fi
	if [ ! -r ${DEST_CONF} ]; then
		echo "unable to find or read ${DEST_CONF}"
		return 0
	fi
	echo "ok"
	return 1
}

#
# check for log file writability
#
# usage: check_log_file
#
function check_log_files() {
	echo -n "checking log files... "
	if [ "${LOG_FILE}" == "-" ]; then
		echo "stdout:ok"
		return 1
	fi
	if [ ! -d ${LOG_DIR} ] || [ ! -x ${LOG_DIR} ]; then
		echo "unable to find or read ${LOG_DIR}"
		return 0
	fi
	if [ ! -w ${LOG_FILE} ]; then
		echo "unable to find or write ${LOG_FILE}"
		return 0
	fi
	echo "${LOG_FILE}:ok"
	return 1
}

#
# check external binary dependecies
#
# usage: check_dep
#
function check_dep() {
	echo -n "checking dependencies... "
	if [ ! -x ${CAT} ]; then
		echo "unable to execute or find ${CAT}"
		return 0
	fi
	if [ ! -x ${DATE} ]; then
		echo "unable to execute or find ${DATE}"
		return 0
	fi
	if [ ! -x ${SSH} ]; then
		echo "unable to execute or find ${SSH}"
		return 0
	fi
	if [ ! -x ${SCP} ]; then
		echo "unable to execute or find ${SCP}"
		return 0
	fi
	if [ ! -x ${CUT} ]; then
		echo "unable to execute or find ${CUT}"
		return 0
	fi
	if [ ! -x ${EGREP} ]; then
		echo "unable to execute or find ${EGREP}"
		return 0
	fi
	if [ ! -x ${RM} ]; then
		echo "unable to execute or find ${RM}"
		return 0
	fi
	if [ ! -x ${MKDIR} ]; then
		echo "unable to execute or find ${MKDIR}"
		return 0
	fi
	echo "ok"
	return 1
}

#
# check for directory existence and create it if necessary
#
# usage: new_dir directory_path
# return: 0 - ok, directory created; 1 - ok, directory already existed
#
new_dir()
{
        local DIR=${1}
        do_time
        echo -n "${TIME} - checking dir ${DIR}... "
        if [ ! -d ${DIR} ]; then
                OLD=0
                echo -n "creating... "
                ${MKDIR} -p ${DIR}
                echo -n "done: "
        else
                OLD=1
                echo -n "already exists: "
        fi

        # generate a return value: check for directory
        # existence and permissions
        if [ ! -d ${DIR} ] || [ ! -x ${DIR} ]; then
                echo "unable to use directory"
                return $((${OLD}+1))
        else
                echo "ok"
                return 0;
        fi
}

#
# parse $FILES_CONF configuration files and generate a
# string with all the files to be retrieved
# That string will be placed on the global variable ${FILES}
#
# usage: parse_files_config $CONFIG_FILE
function parse_files_config() {
	local CONFIG_FILE=${1}
	do_time
	echo "${TIME} - parsing files config file (${CONFIG_FILE}):"
	# find and create list of files from ${CONFIG_FILE}
	while read LINE; do

		#USERNAME=$(echo ${LINE} | cut -d ':' -f 1)
		#FILES=$(echo ${LINE} | cut -d ':' -f 2)
		#echo "  * username: ${USERNAME}"

		if [ "${FILES}" == "" ]; then
			FILES=${LINE}
		else
			FILES="${FILES} ${LINE}"
		fi
	done < <(${CAT} ${CONFIG_FILE} | ${EGREP} -v "${LINE_IGNORE}")
	echo "  * files: ${FILES}"
}

#
# parse $DEST_CONF configuration files and generate a
# a global variables with DESTINATION
#
# usage: parse_dest_config $CONFIG_FILE
function parse_dest_config() {
	local CONFIG_FILE=${1}
	do_time
	echo "${TIME} - parsing destination config file (${CONFIG_FILE}):"
	while read LINE; do
		DESTINATION=${LINE}
	done < <(${CAT} ${CONFIG_FILE} | ${EGREP} -v "${LINE_IGNORE}")
	echo "  * destination: ${DESTINATION}"
}

#
# parse $HOSTS_CONF configuration files and generate a
# string with all the hosts to be retrieved
# That string will be placed on the global variable ${HOSTS}
#
# usage: parse_hosts_config $CONFIG_FILE
function parse_hosts_config() {
	local CONFIG_FILE=${1}
	do_time
	echo "${TIME} - parsing hosts config file (${CONFIG_FILE}):"
	# find and create list of hosts from ${CONFIG_FILE}
	while read LINE; do
		if [ "${HOSTS}" == "" ]; then
			HOSTS="${LINE}"
		else
			HOSTS="${HOSTS} ${LINE}"
		fi
		echo "  * ${LINE}"
	done < <(${CAT} ${CONFIG_FILE} | ${EGREP} -v "${LINE_IGNORE}")
}

#
# retrieve $files from $usename@$hosts:$destination
#
# usage: retrieve "$files" "$hosts" "$username" "$destination"
function retrieve() {
	local FILES=$(echo ${1} | tr -s ' ' ',')
	local HOSTS=${2}
	local USERNAME=${3}
	local DESTINATION=${4}

	for HOST in ${HOSTS}; do
		do_time
		echo "${TIME} - processing host: ${HOST}"
		DEST=${DESTINATION}/${HOST}
		new_dir ${DEST}
		if [ ! $? = 0 ] && [ ! $? = 1 ]; then
			echo "  * unable to create directory ${DEST}"
			continue
		fi
		${SCP} ${USERNAME}@${HOST}:{${FILES}} ${DEST}/
	done
}

if [ ! "${LOG_FILE}" == "-" ]; then
        # copy stdout into file descriptor 6
        exec 6>&1
        # redirect stdout into logfile
        exec 1>>${LOG_FILE}
fi

# start run log line
do_now
echo "${SL} retrieve.sh start run at ${NOW} ${SR}"

# do all sorts of checks before actually doing real work
check_dirs
if [ $? == 0 ]; then
	exit 1
fi
# check files
check_config_files
if [ $? == 0 ]; then
	exit 1
fi
# check dependencies
check_dep
if [ $? == 0 ]; then
	exit 1
fi
# check log files
check_log_files
if [ $? == 0 ]; then
	exit 1
fi

# parse files config file
parse_files_config ${FILES_CONF}
# parse hosts config file
parse_hosts_config ${HOSTS_CONF}
# parse destination config file
parse_dest_config ${DEST_CONF}

# retrieve $FILES from $USERNAME@$HOSTS:$DESTINATION
retrieve "${FILES}" "${HOSTS}" "${USERNAME}" "${DESTINATION}"

# end run log line
do_now
echo "${SL} retrieve.sh end run   at ${NOW} ${SR}"
