#!/bin/sh
#
# /usr/sbin/oss-mondobackup
# wrapper script for serverbackup with mondo based on linuxmuster-backup
# tschmitt@linuxmuster.de
# fschuett@gymhim.de
#

# date & time
[ -e /usr/bin/date ] && DATETIME=`date +%y%m%d-%H%M%S`
PATH=/sbin:/usr/sbin:/usr/bin:/bin
export PATH

# default values
deplistdir=/etc/mindi/deplist.d
netconf=$deplistdir/net.conf
mondocache=/var/cache/mondo
cachefiles="difflevel.0"
VERIFY=yes
ISOPREFIX=server
MEDIASIZE=650
EXCLUDEDIRS="/var/tmp"
COMPRESSION=3
KEEPFULL=1
KEEPINC=7

# read config
[ -f /etc/sysconfig/oss-mondobackup ] && . /etc/sysconfig/oss-mondobackup

# parsing parameters
while getopts "fdib:m:r:v:p:z:x:n:s:c:u:F:D:I:" opt; do
  case "$opt" in
    f) full=1 ;;
    d) diff=1 ;;
    i) inc=1 ;;
    b) backupdevice=$OPTARG ;;
    r) restoremethod=$OPTARG ;;
    v) verify=$OPTARG ;;
    p) isoprefix=$OPTARG ;;
    z) mediasize=$OPTARG ;;
    x) excludedirs=$OPTARG ;;
    n) includedirs=$OPTARG ;;
    s) services=$OPTARG ;;
    c) compression=$OPTARG ;;
    u) unmount=$OPTARG ;;
    F) keepfull=$OPTARG ;;
    D) keepdiff=$OPTARG ;;
    I) keepinc=$OPTARG ;;
  esac
done


usage() {
  echo
  echo "Usage: oss-mondobackup -f[ull]|-d[iff]|-i[nc] [options]"
  echo
  echo "Options:"
  echo "-b[ackupdevice] device               : device is a hd partition or a nfs share,"
  echo "                                       e.g. /dev/sdb1 or 10.16.1.10:/home/nfs".
  echo "-m[ountpoint] directory              : directory where the device is mounted."
  echo "-r[estoremethod] hd|nfs              : where you plan to restore from."
  echo "-v[erify] yes|no                     : whether to verify data after backup."
  echo "-[iso]p[refix] server                : string which is used as a prefix for"
  echo "                                       the iso files and the folder."
  echo "-[mediasi[z[e] 4430                  : size of the iso files in mb."
  echo "-[e]x[cludedirs] dir1,dir2,...       : directories to exclude from backup."
  echo "-[i]n[cludedirs] dir1,dir2,...       : directories to include in backup,"
  echo "                                       if not set the whole file system will be backed up."
  echo "-s[ervices] all|service1,service2,...: list of services in the current runlevel which"
  echo "                                       have to be stopped before backup begins,"
  echo "                                       <all> stops all services in the current runlevel,"
  echo "                                       if not set no service will be stopped."
  echo "-c[ompression] 3                     : compression level (0-9), 0 means no compression."
  echo "-u[nmount] yes|no                    : whether to unmount the backupdevice after backup."
  echo "-[keep]F[ull] 1                      : how many full backup sets to keep"
  echo "-[keep]D[iff] 1                      : how many differential backup sets to keep."
  echo "-[keep]I[nc] 7                       : how many incremental backup sets to keep."
  echo "                                       keep values have to be greater than 0."
  echo
  exit 1
}


# test options
[[ -n "$backupdevice" ]] && BACKUPDEVICE=$backupdevice
[[ -n "$mountpoint" ]] && MOUNTPOINT=$mountpoint
[[ -n "$restoremethod" ]] && RESTOREMETHOD=$restoremethod
[[ -n "$verify" ]] && VERIFY=$verify
[[ -n "$isoprefix" ]] && ISOPREFIX=$isoprefix
[[ -n "$mediasize" ]] && MEDIASIZE=$mediasize
[[ -n "$excludedirs" ]] && EXCLUDEDIRS=$excludedirs
[[ -n "$includedirs" ]] && INCLUDEDIRS=$includedirs
[[ -n "$services" ]] && SERVICES=$services
[[ -n "$compression" ]] && COMPRESSION=$compression
[[ -n "$unmount" ]] && UNMOUNT=$unmount
[[ -n "$keepfull" ]] && KEEPFULL=$keepfull
[[ -n "$keepdiff" ]] && KEEPDIFF=$keepdiff
[[ -n "$keepinc" ]] && KEEPINC=$keepinc

[[ -n "$full" && -n "$inc" ]] && usage
[[ -n "$full" && -n "$diff" ]] && usage
[[ -n "$inc" && -n "$diff" ]] && usage
[[ -z "$BACKUPDEVICE" || -z "$MOUNTPOINT" ]] && usage


# test if variable is an integer
isinteger () {
  [ $# -eq 1 ] || return 1

  case $1 in
  *[!0-9]*|"") return 1;;
            *) return 0;;
  esac
} # isinteger


# set mondoarchive parameter
if [ -n "$full" ]; then

	isinteger $KEEPFULL || usage
	btypeparam="-O"
	btype=full
	msg="Full"

elif [ -n "$diff" ]; then

	isinteger $KEEPDIFF || usage
	btypeparam="-D -O"
	btype=diff
	msg="Differential"

elif [ -n "$inc" ]; then

	isinteger $KEEPINC || usage
	btypeparam="-D -O"
	btype=inc
	msg="Incremental"

else

	usage

fi
[ "$VERIFY" != "no" ] && btypeparam="${btypeparam} -V"
bfolder=${DATETIME}_${btype}


# warning
echo "$msg backup starts in 5 seconds! Press CTRL-C to cancel!"
sleep 5


# check if i am already running
mypid=$$
mypidfile=/var/run/oss-mondobackup.pid
if [ -e "$mypidfile" ]; then
    echo "There is already a oss-mondobackup process running! Exiting!"
    echo "If this is not correct you have to delete $mypidfile!"
    exit 0
fi
echo "$mypid" > $mypidfile


# exit with error
error() {
	echo "$1 Exiting!"
	rm $mypidfile
	exit 1
} # error

# create MOUNTPOINT if necessary
[ -d "$MOUNTPOINT" ] || mkdir -p $MOUNTPOINT
if [ ! -d "$MOUNTPOINT" ]; then
	error "MOUNTPOINT $MOUNTPOINT does not exist!"
fi 

# check if BACKUPDEVICE is already mounted
if mount | grep "$BACKUPDEVICE" | grep -q "$MOUNTPOINT"; then

    echo "$BACKUPDEVICE is already mounted on $MOUNTPOINT!"
    mounted=yes

fi


# mount if necessary and set params depending on RESTOREMETHOD
case $RESTOREMETHOD in

    hd)
	if [ -z "$mounted" ]; then
	    echo "Trying to mount $BACKUPDEVICE on $MOUNTPOINT ..."
	    mount $BACKUPDEVICE $MOUNTPOINT || error "Cannot mount $BACKUPDEVICE!"
	    mounted=yes
	fi
	rtypeparam="-i"
	target=$MOUNTPOINT/$ISOPREFIX/$bfolder
	;;

    nfs)
	if [ -z "$mounted" ]; then
	    echo "Trying to mount $BACKUPDEVICE on $MOUNTPOINT ..."
	    mount -t nfs -o rsize=8192,wsize=8192 $BACKUPDEVICE $MOUNTPOINT || error "Cannot mount $BACKUPDEVICE!"
	    mounted=yes
	fi
	rtypeparam="-n $BACKUPDEVICE"
	target=$ISOPREFIX/$bfolder
	;;

    *)
	echo "Unknown restore method!"
	error
	;;

esac

if [ -n "$mounted" ]; then
	echo "$BACKUPDEVICE successfully mounted on $MOUNTPOINT!"
else
	error "$BACKUPDEVICE not mounted!"
fi


# remove MOUNTPOINT from EXCLUDEDIRS, will be added directly to mondoarchive parameters below (#458).
EXCLUDEDIRS="${EXCLUDEDIRS//$MOUNTPOINT/}"


# converting lists (see #462)
EXCLUDEDIRS="${EXCLUDEDIRS//,/|}"
INCLUDEDIRS="${INCLUDEDIRS//,/|}"
SERVICES="${SERVICES//,/ }"
ALLSERVICES="$(/usr/bin/systemctl list-units --type=service --state=running|grep '\.service'|cut -d\. -f1|tr "\n" " ")"

# included dirs?
# workaround mondoarchive segfault
[ "$INCLUDEDIRS" = "/" ] && INCLUDEDIRS=


# shut down SERVICES
if [ -n "$SERVICES" ]; then
	echo "Shutting down SERVICES ..."
	if [ "$SERVICES" = "all" ]; then
		if [ -n "$ALLSERVICES" ]; then
			/usr/bin/systemctl stop $ALLSERVICES
		fi
	else
		/usr/bin/systemctl stop $SERVICES
	fi
fi


# start archiving process
echo
echo "Starting $msg backup process with following options:"
echo "  BACKUPDEVICE=$BACKUPDEVICE"
echo "  MOUNTPOINT=$MOUNTPOINT"
echo "  RESTOREMETHOD=$RESTOREMETHOD"
echo "  VERIFY=$VERIFY"
echo "  SERVICES=$SERVICES"
echo "  ISOPREFIX=$ISOPREFIX"
echo "  bfolder=$bfolder"
echo "  INCLUDEDIRS=$INCLUDEDIRS"
echo "  EXCLUDEDIRS=$EXCLUDEDIRS"
echo "  MEDIASIZE=$MEDIASIZE"
echo "  COMPRESSION=$COMPRESSION"
echo "  UNMOUNT=$UNMOUNT"
echo "  btypeparam=$btypeparam"
echo "  rtypeparam=$rtypeparam"
echo "  target=$target"
echo

# storing backup time in unix format
backuptime=`date +%s`

RC=0

# determine last backup set
case $btype in
	diff)
		# difflevel of last full backup
		if sets=`ls -dr $MOUNTPOINT/$ISOPREFIX/*-*_full`; then
			lastset=`echo $sets | awk '{ print $1 }'`
			echo "Found last full backup set in $lastset."
		else
			echo "Last full backup set not found!"
			RC=1
		fi
		;;
	inc)
		# difflevel of last backup (full, diff or inc)
		if sets=`ls -dr $MOUNTPOINT/$ISOPREFIX/*-*_* | grep -G _[dfi][inu][cfl]`; then
			lastset=`echo $sets | awk '{ print $1 }'`
			echo "Found last backup set in $lastset."
		else
			echo "Last backup set not found!"
			RC=1
		fi
		;;
	*)
		;;
esac

# restore mondo-archive cache if no full backup
if [ "$btype" != "full" ]; then
	if [ -d "$lastset/cache" ]; then
		# test if all cachefiles are present
		echo "Testing cachefiles in $lastset/cache ..."
		for i in $cachefiles; do
			[ -e "$lastset/cache/$i" ] || RC=1
		done
		if [ "$RC" -eq 0 ]; then
			echo "Restoring cache from $lastset/cache ..."
			rsync -a --delete $lastset/cache/ $mondocache/
		else
			echo "At least one of $cachefiles is missing!"
			echo "Cannot restore cache!"
		fi
	else
		echo "$lastset/cache does not exist! Cannot restore cache!"
		RC=1
	fi
fi


# create backup folder if necessary
[ -d "$MOUNTPOINT/$ISOPREFIX/$bfolder/cache" ] || mkdir -p $MOUNTPOINT/$ISOPREFIX/$bfolder/cache
if [ ! -d "$MOUNTPOINT/$ISOPREFIX/$bfolder/cache" ]; then
	[ -d "$MOUNTPOINT/$ISOPREFIX/$bfolder" ] && rm -rf $MOUNTPOINT/$ISOPREFIX/$bfolder
	echo "Cache directory $MOUNTPOINT/$ISOPREFIX/$bfolder/cache does not exist!"
	RC=1
fi 


# remove BACKUPDEVICE from fstab
if [ $RC -eq 0 ]; then
	if grep -q ^$BACKUPDEVICE /etc/fstab; then
		echo "Removing backup device $BACKUPDEVICE from /etc/fstab!"
		fstabtmp=/var/tmp/fstab.$$
		cp -f /etc/fstab $fstabtmp || RC=1
		grep -v $BACKUPDEVICE $fstabtmp > /etc/fstab || RC=1
	fi
fi


# portmap is needed in mondorescue's iso file system
[ -d "$deplistdir" ] || mkdir -p $deplistdir
[ -e "$netconf" ] || touch "$netconf"
if ! grep -q ^/sbin/portmap "$netconf"; then
 echo "Adding /sbin/portmap to $netconf ..."
 echo "/sbin/portmap" >> "$netconf"
fi

# specify kernel manually (from kernel > 3.8)
kernelver=$(uname -r)
kernelver1=${kernelver%%.*}
kernelver2=${kernelver#*.}
kernelver2=${kernelver2%%.*}
kernelmanual=
if [  -n "$kernelver1" -a -n "$kernelver2" ]; then
 if [ $kernelver1 -ge 3 -a $kernelver2 -gt 8 ]; then
  kernelmanual="-k /boot/vmlinuz-$(uname -r)"
 fi
else
 RC=1
fi

# starting mondoarchive
if [ $RC -eq 0 ]; then
	if [ -z "$INCLUDEDIRS" ]; then
		/usr/bin/nice -n 19 mondoarchive $btypeparam -p $ISOPREFIX $rtypeparam -E "${MOUNTPOINT}|${EXCLUDEDIRS}" -N -d $target -s ${MEDIASIZE}m -$COMPRESSION -L $kernelmanual
	else
		/usr/bin/nice -n 19 mondoarchive $btypeparam -p $ISOPREFIX $rtypeparam -I "$INCLUDEDIRS" -E "${MOUNTPOINT}|${EXCLUDEDIRS}" -N -d $target -s ${MEDIASIZE}m -$COMPRESSION -L $kernelmanual
	fi
	RC=$?
fi


# restoring fstab
if [ -n "$fstabtmp" -a -e "$fstabtmp" ]; then
	echo "Restoring /etc/fstab!"
	mv -f $fstabtmp /etc/fstab
fi


# if mondoarchive exits cleanly
if [ "$RC" = "0" ]; then

	echo "mondoarchive finished successfully!"

	# store cache files
	echo "Storing cache to $MOUNTPOINT/$ISOPREFIX/$bfolder/cache ..."
	rsync -a --delete $mondocache/ $MOUNTPOINT/$ISOPREFIX/$bfolder/cache/

	case $btype in

		full)
			# copying mondorescue.iso to backup folder if it is a full backup
			if [ -f /var/cache/mindi/mondorescue.iso ]; then
				# mindi 2.22, new location for mindi files
				mondoiso=/var/cache/mindi/mondorescue.iso
			elif [ -f /root/images/mindi/mondorescue.iso ]; then
				mondoiso=/root/images/mindi/mondorescue.iso
			fi
			if [ -n "$mondoiso" ]; then
				echo "Copying mondorescue.iso to $MOUNTPOINT/$ISOPREFIX/$bfolder ..."
				cp -a $mondoiso $MOUNTPOINT/$ISOPREFIX/$bfolder
			else
				echo "Fatal! Cannot find mondorescue.iso!"
				RC=1
			fi
			setstokeep=$KEEPFULL
			;;

		diff)
			setstokeep=$KEEPDIFF
			# store difflevel
			echo $backuptime > $MOUNTPOINT/$ISOPREFIX/$bfolder/cache/difflevel.0
			;;

		inc)
			setstokeep=$KEEPINC
			echo $backuptime > $MOUNTPOINT/$ISOPREFIX/$bfolder/cache/difflevel.0
			;;

	esac

	# cleaning up backup sets
	if sets=`ls -dr $MOUNTPOINT/$ISOPREFIX/*_$btype`; then

		n=1
		echo "Keeping $setstokeep $btype backup sets."

		for s in $sets; do

			if [ "$n" -gt "$setstokeep" ]; then

				echo "Deleting $btype backup set "$s" ..."
				rm -rf $s

			fi

			let n+=1

		done

	fi

else

	echo "Mondoarchive finished with error!"

	if mount | grep mondo | grep -q tmpfs; then

		echo "Unmounting shared memory ..."
		umount /dev/shm

	fi

	if [ -d "$MOUNTPOINT/$ISOPREFIX/$bfolder" ]; then

		echo "Removing $MOUNTPOINT/$ISOPREFIX/$bfolder ..."
		rm -rf $MOUNTPOINT/$ISOPREFIX/$bfolder

	fi

fi # RC = 0


# cleaning up tmp dirs
for i in $MOUNTPOINT /home; do

	echo "Cleaning up tmp dirs on $i ..."
	rm -rf $i/mondo.scratch.*
	rm -rf $i/tmp.mondo.*

done


# unmounting backup media if requested
if [ "$UNMOUNT" = "yes" ]; then

    echo "Trying to unmount $BACKUPDEVICE as requested ..."
    umount $BACKUPDEVICE && echo "Successfully unmounted $BACKUPDEVICE!"

else

    echo "Not unmounting $BACKUPDEVICE as requested!"

fi


# start SERVICES again
if [ -n "$SERVICES" ]; then
	echo "Starting SERVICES again ..."
	if [ "$SERVICES" = "all" ]; then
		if [ -n "$ALLSERVICES" ]; then
			/usr/bin/systemctl start $ALLSERVICES
		fi
	else
		/usr/bin/systemctl start $SERVICES
	fi
fi


# delete temp files
rm $mypidfile


# logging exit status
echo $RC > $LOGDIR/oss-mondobackup.status


# all done
if [ "$RC" = "0" ]; then
	echo "Done! :-)"
else
	echo "Done with mondoarchive error! :-("
fi


exit $RC

