#!/bin/bash
#
# File: /usr/local/bin/backuptodisk
#
# Make up to four Backup copies of one or two directory-sets to a writable
# mounted or unmounted mountable file system.
#
#    Copyright (C) 2013  Ingo Kaesmann
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# Script template: 0.3.0
#
# Exit codes:
# 0 = OK
# 1 = Error in parameters
# 2 = Error in configfile, configfile old or not found
# 3 = Library old or not found
# 8 = Error in other file or other file not found
# 99= Other error
##############################################################################
# SOURCE FILES

. /usr/local/etc/main-inka-sh.conf			# main config
. $LLIBDIR/lib-inka-std.sh				# functions standard
test -e ~/.backuptodiskrc && . ~/.backuptodiskrc	# local config
##############################################################################
# DECLARATION OF VARIABLES

# STRINGS
pkgname=inka-backuptodisk				# package name to work
lib_inka_std_sh_needed=1.3.6				# standard lib version
conf_file_needed=0.3.6					# conf file version
#----------------------- end of variables to configure -----------------------
version=$(grep "^VERSION" $PROGREG/$pkgname.inf | cut -f 2)	# version
scriptname=$(basename $0)				# name of script
tempfile=$TEMPDIR/$scriptname.$UID.$$.tmp		# temporary file
infolog="logger -p user.info -t $scriptname --"		# log info messages
errlog="logger -p user.err -t $scriptname -- ERROR:"	# log error messages
debuglog="logger -p user.debug -t $scriptname -- DEBUG:" # log debug messages
debug=no						# debug=yes/no
verbose=""						# verbose=""/-q/-v
opt_found=no						# opt_found=yes/no
opt=""							# option
#------------------------- end of standard variables -------------------------
ilogf=~/.backuptodisk.log			# intern logfile
attr=""						# unix-filesystem yes/no
automnt=""					# automounting yes/no
bckdir=""					# backup dir
bckrdir=""					# upper backup dir
mntpt=""					# mountpoint
nrbckdir=""					# number of backup dirs
srcdir=""					# source dir

# NUMBERS
err=0							# exit code
##############################################################################
# FUNCTIONS

function show_help ()
{
# Show help
# Uses: cat
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_help: $# $@" >&2
	#$debuglog show_help: $# $@
fi

cat <<EOT

Aufruf: $scriptname OPTION...
Kopiert Verzeichnisse und deren Unterverzeichnisse auf ein Backup-Medium

        -a      Backup-Standard und Backup-Erweitert ausfuehren
        -s      Backup-Standard ausfuehren
        -e      Backup-Erweitert ausfuehren
        -l      Logdatei anzeigen
-h, --help      Hilfe anzeigen und beenden
 --version      Versionsinformationen anzeigen und beenden
--show-config   Programmkonfiguration anzeigen und beenden
   --debug      Debug-Modus benutzen
        -q      ohne Meldungen
        -v      mit vielen Erklaerungen der durchgefuehrten Taetigkeiten

EOT
return
}
#-----------------------------------------------------------------------------
function show_config ()
{
# Show configuration of program
# Uses: cat, cut, grep
# Return codes: standard

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG show_config: $# $@" >&2
	#$debuglog show_config: $# $@
fi

cat <<EOT

Backup-Dateisystem wird automatisch / vor dem Aufruf des Programms gemountet?
Backup-Standard:  $Automnts
Backup-Erweitert: $Automnte
Anzahl der Kopien?
Backup-Standard:  $Nrbckdirs
Backup-Erweitert: $Nrbckdire
Sicherung erfolgt auf Linux-Dateisystem?
Backup-Standard:  $Attrs
Backup-Erweitert: $Attre
Wartezeit in Sekunden vor dem Start: $Wtime
Backup-Standard-Verzeichnis (X=Nummer) 1. Backupmedium:
$Mntpts1/$Bckrdirs/${Bckdirs}.X
Backup-Erweitert-Verzeichnis (X=Nummer) 1. Backupmedium:
$Mntpte1/$Bckrdire/${Bckdire}.X
Backup-Standard-Verzeichnis (X=Nummer) 2. Backupmedium:
$Mntpts2/$Bckrdirs/${Bckdirs}.X
Backup-Erweitert-Verzeichnis (X=Nummer) 2. Backupmedium:
$Mntpte2/$Bckrdire/${Bckdire}.X
Zu sichernde Verzeichnisse Backup-Standard:
$Srcdirs
Zu sichernde Verzeichnisse Backup-Erweitert:
$Srcdire
Klangdatei, die beim Beenden der Kopieraktion erklingt:
$Endsnd
EOT
aplay -q $Endsnd

return
}
#-----------------------------------------------------------------------------
function make_backup ()
{
# Mount fs, copy directorys to backup-directory extended and unmount fs
# Uses: cat, cp, echo, ls, mkdir, mount, rm, umount
# Return codes: standard

if [ "$debug" == "yes" ]; then			# debug
	echo "DEBUG show_by_date: $# $@" >&2
fi

local backdir elogf log ret
backdir=""					# for writing logfiles
elogf="$mntpt/$bckrdir/${bckdir}.log"		# extern logfile
log=""						# content of extern logfile

ret=0						# returncode of command

if [ "$automnt" == "no" ]; then			# automounting yes/no
	mount $mntpt
	ret=$?
	if [ $ret -eq 0 ]; then			# filesystem is mounted
		if [ "$verbose" == "-v" ]; then
			echo "Dateisystem wurde gemounted."
		fi
	else					# mounting not possible
		if [ "$verbose" != "-q" ]; then
			echo "Fehler - Dateisystem konnte nicht gemountet werden!" >&2
		else
			$errlog "mounting filesystem not possible"
		fi
		exit 99
	fi
fi
if [ -e "$elogf" ]; then			# extern logfile exist
	case "$nrbckdir" in
		1)				# only 1 backup-copy
			backdir=${bckdir}.1;;
		2)				# 2 backup-copys
			log=$(cat $elogf)
			case "$log" in
				${bckdir}.1)	backdir=${bckdir}.2;;	# switch from 1 to 2
				${bckdir}.2)	backdir=${bckdir}.1;;	# switch from 2 to 1
				""|*)	# extern logfile wrong or no file or fs not mounted
					if [ "$verbose" != "-q" ]; then
						echo "Fehler - Externe Logdatei enthaelt falschen Wert," >&2
						echo "oder Datei fehlt oder Dateisystem nicht gemountet." >&2
						echo "Kein Backup erstellt." >&2
					else
						$errlog "wrong value in ext. logfile, or missing, or fs not mounted"
					fi
					exit 8;;
			esac;;
		3)				# 3 backup-copys
			log=$(cat $elogf)
			case "$log" in
				${bckdir}.1)	backdir=${bckdir}.2;;	# switch from 1 to 2
				${bckdir}.2)	backdir=${bckdir}.3;;	# switch from 2 to 3
				${bckdir}.3)	backdir=${bckdir}.1;;	# switch from 3 to 1
				""|*)	# extern logfile wrong or no file or fs not mounted
					if [ "$verbose" != "-q" ]; then
						echo "Fehler - Externe Logdatei enthaelt falschen Wert," >&2
						echo "oder Datei fehlt oder Dateisystem nicht gemountet." >&2
						echo "Kein Backup erstellt." >&2
					else
						$errlog "wrong value in ext. logfile, or missing, or fs not mounted"
					fi
					exit 8;;
			esac;;
		4)				# 4 backup-copys
			log=$(cat $elogf)
			case "$log" in
				${bckdir}.1)	backdir=${bckdir}.2;;	# switch from 1 to 2
				${bckdir}.2)	backdir=${bckdir}.3;;	# switch from 2 to 3
				${bckdir}.3)	backdir=${bckdir}.4;;	# switch from 3 to 4
				${bckdir}.4)	backdir=${bckdir}.1;;	# switch from 4 to 1
				""|*)	# extern logfile wrong or no file or fs not mounted
					if [ "$verbose" != "-q" ]; then
						echo "Fehler - Externe Logdatei enthaelt falschen Wert," >&2
						echo "oder Datei fehlt oder Dateisystem nicht gemountet." >&2
						echo "Kein Backup erstellt." >&2
					else
						$errlog "wrong value in ext. logfile, or missing, or fs not mounted"
					fi
					exit 8;;
			esac;;
		*)			# wrong number of backup-copys in configfile
			if [ "$verbose" != "-q" ]; then
				echo "Fehler - Falsche Anzahl von Backup-Kopien in .backuprc eingetragen!" >&2
				echo "Kein Backup erstellt." >&2
			else
				$errlog "config file contains wrong value in backupcopies"
			fi
			exit 2;;
	esac
else						# extern logfile not exist
	backdir=${bckdir}.1
fi

if [ "$verbose" == "-v" ]; then			# delete old backup
	rm -rv $mntpt/$bckrdir/$backdir/*
	mkdir -pv $mntpt/$bckrdir/$backdir	# make bakdir verbose, if not exist
else
	rm -r $mntpt/$bckrdir/$backdir/*
	mkdir -p $mntpt/$bckrdir/$backdir	# make bakdir quiet, if not exist
fi

if [ "$attr" == "yes" ]; then			# backup-fs is linux-fs
	ret=0
	if [ "$verbose" == "-q" ]; then		# create backup
		cp --parents -at $mntpt/$bckrdir/$backdir $srcdir
	else
		cp --parents -avt $mntpt/$bckrdir/$backdir $srcdir
	fi
	ret=$?					# error level of cp
elif [ "$attr" == "no" ]; then			# backup-fs is no linux-fs
	ret=0
	if [ "$verbose" == "-q" ]; then		# create backup
		cp --parents --preserve=timestamps -PRt $mntpt/$bckrdir/$backdir $srcdir
	else
		cp --parents --preserve=timestamps -PRvt $mntpt/$bckrdir/$backdir $srcdir
	fi
	ret=$?					# error level of cp
else						# wrong value of Attrs in configfile
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Falscher Wert von 'Attre' in .backuprc eingetragen!" >&2
		echo "Kein Backup erstellt." >&2
	else
		$errlog "value from Attre in config file wrong"
	fi
	exit 2
fi

# error if trying copy empty directories
#if [ $ret -ne 0 ]; then			# error by cp
#	if [ "$verbose" != "-q" ]; then
#		echo "Fehler - Der Befehl 'cp' (Kopieren) gab einen Fehler zurueck!" >&2
#		echo "Moegliche Ursachen: Quellverzeichnis oder Zielverzeichnis nicht" >&2
#		echo "vorhanden, keine Lese- oder Schreibberechtigung, falsches" >&2
#		echo "Dateisystem oder Speichermedium voll." >&2
#		echo "Evtl. kein (vollstaendiges) Backup erstellt." >&2
#		echo "Kein Eintrag in Backup-Logdateien vorgenommen." >&2
#		echo "Daher erfolgt naechster Backup-Versuch in gleiche Backup-Kopie." >&2
#	else
#		$errlog "copy of files failed"
#	fi
#	err=99
#else
	echo $backdir > $elogf			# refresh extern logfile
	/bin/ls -dgG --time-style=long-iso $mntpt/$bckrdir/$backdir  | cut -d " " -f 4-6 >> $ilogf
#fi

if [ "$automnt" == "no" ]; then			# automounting yes/no
	umount $mntpt
	ret=$?
	if [ $ret -eq 0 ]; then			# fileystem is unmounted
		if [ "$verbose" == "-v" ]; then
			echo "Dateisystem wurde ungemounted."
		fi
	else					# unmounting not possible
		if [ "$verbose" != "-q" ]; then
			echo "Fehler - Dateisystem konnte nicht ungemountet werden!" >&2
		else
			$errlog "unmounting filesystem not possible"
		fi
		err=99
	fi
fi

return $err
}
#-----------------------------------------------------------------------------
function main_proc ()
{
# Main program for backup
# Parameter1: -s or -e
# Uses: cat, echo, wc, Read_Lines
# Return codes: standard

if [ "$debug" == "yes" ]; then			# debug
	echo "DEBUG do_it: $# $@" >&2
	#$debuglog do_it: $# $@
fi

local p1=$1 f='' m='' i=0 k=0 l=0 ret=0

if [ "$p1" == "-s" ]; then
	attr=$Attrs
	automnt=$Automnts
	bckdir=$Bckdirs
	bckrdir=$Bckrdirs
	mntpt=$Mntpts1
	nrbckdir=$Nrbckdirs
	srcdir=$Srcdirs
elif [ "$p1" == "-e" ]; then
	attr=$Attre
	automnt=$Automnte
	bckdir=$Bckdire
	bckrdir=$Bckrdire
	mntpt=$Mntpte1
	nrbckdir=$Nrbckdire
	srcdir=$Srcdire
fi

if [ -e $ilogf ];then
	l=$(cat $ilogf | wc -l)			# number of lines of intern log file
	i=$l
	while [ $i -gt 0 ]; do			# look what is last mountpoint for $bckdir
		line=$(Read_Lines $ilogf $i $i)
		let i--
		f=$(echo $line | grep $bckdir)
		if [ $? -eq 0 ]; then		# $bckdir found in line

			# set mountpoint (mntpt)
			m=$(echo $line | grep $Mntpts1)
			ret=$?
			if [ $ret -eq 0 ] && [ "$p1" == "-s" ]; then
				mntpt=$Mntpts2
			fi

			m=$(echo $line | grep $Mntpts2)
			ret=$?
			if [ $ret -eq 0 ] && [ "$p1" == "-s" ]; then
				mntpt=$Mntpts1
			fi

			m=$(echo $line | grep $Mntpte1)
			ret=$?
			if [ $ret -eq 0 ] && [ "$p1" == "-e" ]; then
				mntpt=$Mntpte2
			fi

			m=$(echo $line | grep $Mntpte2)
			ret=$?
			if [ $ret -eq 0 ] && [ "$p1" == "-e" ]; then
				mntpt=$Mntpte1
			fi

			i=0			# stop loop
		fi
	done
fi

if [ "$verbose" != "-q" ]; then
	k=$Wtime
	echo "Dateisystem $mntpt anschliessen."
	while [ $k -gt 0 ]; do
		echo -e "Datensicherung startet in $k Sekunden  \r\c"
		sleep 1s
		let k--
	done
	echo
else
	sleep ${Wtime}s
fi

echo "Datensicherung gestartet"

make_backup					# make backup
aplay -q $Endsnd				# play sound

return
}
#-----------------------------------------------------------------------------
function show_logfile ()
{
# Show intern logfile
# Uses: cat, echo, $PAGER
# Return codes: standard

if [ "$debug" == "yes" ]; then			# debug
	echo "DEBUG show_by_date: $# $@" >&2
fi

cat $ilogf | $PAGER				# show intern logfile in $PAGER

return
}
##############################################################################
# PARSE COMMANDLINE

# Read all options with args and all additional args into variables.
# Stop parsing after last parameter.
# Try to do a check on all parameters and version numbers.
# Handle options -h, -C and -V directly.
# Exit on error.

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG Parse Commandline: $# $@" >&2
	#$debuglog Parse Commandline: $# $@
fi

# get options and options with arguments
while [ "${1:0:1}" == "-" ]; do				# get all options
	case "$1" in
	  -h|--help)	opt=-h;;			# option -h
	  --version)	opt=--v;; 			# option --version
	  --show-config) opt=--s;;			# option --show-config
	  --debug)	debug=yes;;			# option --debug
	  -q|--quiet)	verbose=-q;;			# option -q
	  -v|--verbose)	verbose=-v;;			# option -v
#-------------------------- end of standard options --------------------------
	  -a|-e|-l|-s)	opt="$1";;			# opt -a,-e,-l,-s
	  *)	if [ "$verbose" != "-q" ]; then
			echo "Fehler - Option $1 falsch!" >&2	# error
		else
			$errlog unknown option $1
		fi
		exit 1;;
	esac
	shift
	opt_found=yes
done

# handle standard options -h, -V, -C
case "$opt" in
  -h)	show_help; exit 0;;					# show help
  --v)	echo "$scriptname (${pkgname}) $version"; exit 0;;	# show version
  --s)	show_config; exit 0;;					# show config
esac

# check library
Compare_Version $lib_inka_std_sh_needed $Lib_Inka_Std_Sh_Version
ret=$?						# (ret: 0,1,2,9,127)
if [ $ret -eq 1 ]; then				# needed lib-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Programm benoetigt neuere Version von lib-inka-std.sh!" >&2
	else
		$errlog newer version of lib-inka-std.sh needed
	fi
	exit 3
elif [ $ret -eq 9 ] || [ $ret -eq 127 ]; then	# other error (version or lib missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung von lib-inka-std.sh nicht moeglich!"
	else
		$errlog check of version of lib-inka-std.sh impossible
	fi
	exit 3
else						# check ok, reset $ret
	ret=0
fi

# check version of config files			# set config var!
#Compare_Version $conf_file_needed $Program_Conf
Compare_Version $conf_file_needed $Backuptodisk_Rc
ret=$?						# (ret: 0,1,2,9)
if [ $ret -eq 1 ]; then				# needed config-version higher than installed
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - $scriptname benoetigt neuere Version der Konfig-datei!" >&2
	else
		$errlog newer version of conf file needed
	fi
	exit 2
elif [ $ret -eq 9 ]; then			# other error (version of conffile missing)
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Versionspruefung der Konfig-datei von $scriptname nicht moeglich!"
	else
		$errlog check of version of conf file impossible
	fi
	exit 2
else						# check ok, reset $ret
	ret=0
fi

# check arguments after options
case "$opt" in
  *)	if [ "$#" -ne 0 ]; then				# error in param count
		if [ "$verbose" != "-q" ]; then
			echo "Fehler - Parameteranzahl falsch!" >&2
		else
			$errlog parameter count
		fi
		exit 1
	fi;;
esac

# test variables, must be different
if [ "$Mntpts/$Bckrdirs/$Bckdirs" == "$Mntpte/$Bckrdire/$Bckdire" ]; then
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - In Konfigurationsdatei wurden fuer Backup-Standard-Pfad" >&2
		echo "und Backup-Erweitert-Pfad die gleichen Werte angegeben." >&2
	else
		$errlog "backup-standard-path and backup-extended-path similar"
	fi
	exit 2
fi
##############################################################################
# MAINPROGRAM

if [ "$debug" == "yes" ]; then				# debug
	echo "DEBUG Main: $# $@" >&2
	#$debuglog Main: $# $@
fi

# Check what to do (initial options and args are removed from cmdline)
case "$opt" in
  -a)	main_proc -s; main_proc -e;;
  -e)	main_proc -e;;
  -s)	main_proc -s;;
  -l)	show_logfile;;
  "")							# no opt given
	if [ "$verbose" != "-q" ]; then
		echo "Fehler - Option fehlt!" >&2
	else
		$errlog option missing
	fi
	err=1;;
esac

exit $err
