#!/bin/bash
#
# @file
# @brief Cella wraps backup tools, defines and runs backup jobs
#
# @author code@edoceo.com
# @copyright 2008-2011 Edoceo, Inc http://edoceo.com/
# @license MIT
# @see http://edoceo.com/creo/cella

#
# Cella does backups of files and databases using standard tools
# rdiff and rsync for file backups
# mysqldump and pg_dump for database backups
# ldapsearch or slapcat for OpenLDAP
# svn-hot-backup for Subversion
# git ?

# ./cella.sh script.sh
# ./cella.sh --log=/var/log/cella.log script.sh
# ./cella.sh --log=/var/log/cella.err --log=/var/log/cella.log script.sh
# ./cella.sh --debug script.sh
# ./cella.sh --verbose script.sh
# ./cella.sh

# I need this to run
_cella_dir=$(dirname $(readlink -f "${BASH_SOURCE[0]}") )
# sourced or executed?
_cella_exp=$( [[ "${BASH_SOURCE[0]}" != "$0" ]] && echo "true" )
_cella_err="/dev/stderr"
_cella_log="/dev/null"
# utility variables
_cella_mysql_host=
_cella_mysql_user=
_cella_mysql_pass=
_cella_pgsql_host=
_cella_pgsql_user=
_cella_pgsql_fmt="custom"
_cella_rdiff_age="21D"
_cella_runas=

set -o allexport
set -o errtrace
set -o noclobber

if [ "$_cella_exp" != "true" ]; then
    set -o errexit
fi

# Show Help
function cella_help()
{
    echo "Cella Shell Based Backup Script"
    if [ "$_cella_exp" != "true" ]; then
        echo "${BASH_SOURCE[0]} --debug --verbose --err=[file] --log=[file] [script]"
    fi

    echo -n "Support:"
    which rsync           >/dev/null 2>&1 && echo -n " rsync"
    which rdiff-backup    >/dev/null 2>&1 && echo -n " rdiff (--age=$_cella_rdiff_age)"
    which mysqldump       >/dev/null 2>&1 && echo -n " mysql"
    which pg_dump         >/dev/null 2>&1 && echo -n " postgres"
    which ldapsearch      >/dev/null 2>&1 && echo -n " ldap"
    which svn-hot-backup  >/dev/null 2>&1 && echo -n " svn"
    echo

    echo
    echo "Update: curl http://cdn.edoceo.com/bin/cella.sh > ${BASH_SOURCE[0]}"
    echo

    if [ "$_cella_exp" == "true" ]; then
        echo
        echo "Cella routines have been exported to the current shell environment"
        echo
    fi

}
export -f cella_help

# Run the next commands as this user via sudo
# @param $1 = Username
function cella_runas()
{
    username=$1
    # Using su won't work cause username comes after command :(
    # _cella_runas="su --command """
    _cella_runas="sudo -H -n -u $username -- "
}
export -f cella_runas

#
# Backup via rdiff-backup
# @param $1 = rdiff source path
# @param $2 = rdiff target path
# @param $3 = rdiff age
function rdiff_backup()
{
    source=$1
    target=$2

    # Use default, else supplied
    maxage=$_cella_rdiff_age
    if [[ $# == 3 ]]; then
        maxage=$3;
    fi

    # Execute
    $_cella_runas rdiff-backup \
        --backup-mode \
        --create-full-path \
        --exclude-other-filesystems \
        --verbosity=4 \
        "$source" \
        "$target" \
        1>>$_cella_log 2>>$_cella_err
		# 2>&1 | ( grep -v 'does not match remote' || true )

    # If maxage specified then trim
    if [[ -n "$maxage" ]]; then
        $_cella_runas rdiff-backup \
            --force \
            --remove-older-than "$maxage" \
            "$target" \
            1>>$_cella_log 2>>$_cella_err
    fi

    #
    # rdiff-backup --list-increment-sizes

    _cella_runas=

    return 0
}
export -f rdiff_backup

#
# Backup via rsync
# @param $1 = rsync source path
# @param $2 = rsync target path
# @param $3 = options to rsync
function rsync_backup()
{
    source=$1
    target=$2
    option=$3

    $_cella_runas rsync \
        --archive \
        --delete-before \
        --one-file-system \
        $option \
        "$source" \
        "$target" \
        1>>$_cella_log 2>>$_cella_err

    _cella_runas=

    return 0
}
export -f rsync_backup

#
# Backup MySQL
# @param $1 = source, "." for all on localhost, "$server/$database" or "$server/."
# @param $2 = target directory
# @param $3 = Username
# @param $4 = Password
function mysql_backup()
{
    # Hostname/Database
    hostname=$(echo "$1"|cut -d'/' -f1)
    database=$(echo "$1"|cut -d'/' -f2)
    if [[ "$hostname" == "$database" ]]; then
        hostname=""
    fi

    # Hostname/Database
    if [[ -n "$hostname" ]]; then
        _cella_mysql_host="--host=$hostname"
    fi

    # Path
    dumppath=$2
    if [[ ! -d "$dumppath" ]]; then
        mkdir -p "$dumppath"
    fi

    # User
    if [[ -n "$3" ]]; then
        _cella_mysql_user="--user=$3"
    fi

    _cella_mysql_pass="--password=$4";

    if [[ "$database" == "." ]]; then
        database=$(mysql_database_list)
    fi

    for name in $database; do

        file="$dumppath/$name.sql"

        $_cella_runas mysqldump \
            $_cella_mysql_host \
            $_cella_mysql_user \
            $_cella_mysql_pass \
            --comments \
            --extended-insert \
            --flush-logs \
            --quick \
            --routines \
            --databases $name \
            --result-file="$file" \
            1>>$_cella_log 2>>$_cella_err

    done

    _cella_runas=

    return 0
}
export -f mysql_backup

#
# Gets a list of the MySQL Databases
function mysql_database_list()
{
    echo "show databases; " | $_cella_runas mysql \
        $_cella_mysql_host \
        $_cella_mysql_user \
        $_cella_mysql_pass \
        --batch \
        --disable-pager \
        --silent \
        --skip-column-names \
        |grep -v -e 'information_schema'
}
export -f mysql_database_list

#
# Backup PostgreSQL
# @param $1 = source, "." for all on localhost, "$server/$database" or "$server/."
# @param $2 = target directory
# @param $3 = Username
# @param $4 = Password
function pgsql_backup()
{
    hostname=$(echo "$1"|cut -d'/' -f1)
    database=$(echo "$1"|cut -d'/' -f2)
    if [[ "$hostname" == "$database" ]]; then
        hostname=""
    fi

    # Hostname/Database
    if [[ -n "$hostname" ]]; then
        _cella_pgsql_host="--host=$hostname"
    fi

    # Path
    dumppath=$2
    if [[ ! -d "$dumppath" ]]; then
        mkdir -p "$dumppath"
    fi

    # User
    if [[ -n "$3" ]]; then
        _cella_pgsql_user="--username=$3"
    fi

    password=$4

# #     # Export PGSQL Variables
# #     export PGHOST=$h
# #     export PGUSER=${args[4]:-postgres}
# #     export PGPASSWORD=

#    $_cella_runas psql \
#        $_cella_pgsql_host \
#        $_cella_pgsql_user \
#        --command="\\du" > "$dumppath/user.tab"

    if [[ "$database" == "." ]]; then
        database=$(pgsql_database_list)
    fi

    for name in $database; do

        $_cella_runas pg_dump \
            $_cella_pgsql_host \
            $_cella_pgsql_user \
            --blobs \
            --format=custom \
            --file="$dumppath/$name.pgd" \
            "$name" \
            1>>$_cella_log 2>>$_cella_err

    done

    _cella_runas=

    return 0
}
export -f pgsql_backup

#
# Get a List of the Databases
function pgsql_database_list()
{
    $_cella_runas psql \
        $_cella_pgsql_host \
        $_cella_pgsql_user \
        --list \
        --tuples-only \
        --no-align \
        --expanded \
        |grep Name|cut -d'|' -f2|grep -v '^template'

}
export -f pgsql_database_list

#
# ldapbackup
# @todo set SIZELIMIT       8000 in /etc/openldap/slapd.conf
# @todo or use slapcat (which may be dangerous?)
function slapd_backup()
{
    source=$1

    $_cella_runas ldapsearch

    # ldapsearch -L -D $BIND_USER -w $BIND_PASS -h $MASTERLDAP_IP -p 389 -b'dc=mydomain,dc=com' -s sub '(objectclass=*)' > $OUTPUT_FILE
    
    # http://supportex.net/2011/02/backup-restore-ldap-database/
    # slapcat -o ldif-wrap=80 -v -l /home/backup/ldap.diff
}

#
# Archive SVN with Hot Backup
# @param $1 = Source, eg: "/opt/edoceo/svn/"
# @param $2 = Target, eg: "/mnt/Raid1/Cella/"
function svn_backup()
{
    source=$1
    target=$2

    if [[ ! -d "$target" ]]; then
        $_cella_runas mkdir -p "$target"
    fi

    $_cella_runas svn-hot-backup \
        --archive-type=gz \
        --num-backups=3 \
        "$source" \
        "$target" \
        1>>$_cella_log 2>>$_cella_err

    _cella_runas=

    return 0
}
export -f svn_backup

#
# Process Cella Options
if [ $# == 0 ]; then
    cella_help
fi
for opt in $@
do
    case "${opt}" in
    update)
        wget -O- http://cdn.edoceo.com/bin/cella.sh > $0
        ;;
    --debug|-x)
        set -o xtrace
        ;;
    --help|-h)
        cella_help
        ;;
    --verbose|-v)
        set -o verbose
        ;;
    --age=*)
        _cella_rdiff_age=${opt#*=}
        ;;
    --err=*)
        _cella_err=${opt#*=}
        ;;
    --log=*)
        _cella_log=${opt#*=}
        ;;
    # All Other Options Assumed to be Executable Scripts
    *)
        # Execute in a sub-shell
        if [[ -f "$opt" ]]; then
            _t0=$(date +%s)
            ( source "$opt" )
            _t1=$(date +%s)
            # logger --id --stderr --tag cella "conf '$opt' took $(( $_t1 - $_t0 )) seconds"
        fi
        ;;
   esac
done
