#!/usr/bin/env bash
#
shopt -s extglob
unset LANGUAGE
unset LANG
#
# rsync-backup written by Christian Schwamborn
# published under GNU General Public License v.2
#
# bugs and suggestions to:
# christian.schwamborn@nswit.de
#
# version 2.2.0 (2024-05-11)
#
# You are using this scrip at your own risk, the author is not responsible
# for data damage or losses in any way.
###############################################################################

# some early checks and assignments
if [ ${BASH_VERSION:0:1} -lt 4 ] || \
   [ -z "${BASH_VERSION}" ]; then

	echo "wrong bash version, use at least 4.0" >&2
	exit 255
fi

conf_path="/etc/rsync-backup"
lib_path="/var/lib/rsync-backup"
log_path="/var/log/rsync-backup"

if [ -e "${conf_path}/binary.conf" ]; then
	source "${conf_path}/binary.conf"
fi

: ${which_bin:="which"}
: ${mv_bin:="mv"}
: ${rm_bin:="rm"}
: ${ln_bin:="ln"}
: ${mkdir_bin:="mkdir"}
: ${rmdir_bin:="rmdir"}
: ${ps_bin:="ps"}
: ${test_bin:="test"}
: ${gzip_bin:="gzip"}
: ${lz4_bin:="lz4"}
: ${buffer_bin:="buffer"}
: ${cut_bin:="cut"}
: ${cat_bin:="cat"}
: ${zcat_bin:="zcat"}
: ${tail_bin:="tail"}
: ${head_bin:="head"}
: ${touch_bin:="touch"}
: ${md5sum_bin:="md5sum"}
: ${md5_bin:="md5"}
: ${logger_bin:="logger"}
: ${mktemp_bin:="mktemp"}
: ${ssh_bin:="ssh"}
: ${rsync_bin:="rsync"}
: ${mount_bin:="mount"}
: ${umount_bin:="umount"}
: ${mountpoint_bin:="mountpoint"}
: ${findmnt_bin:="findmnt"}
: ${sort_bin:="sort"}
: ${file_bin:="file"}
: ${grep_bin:="grep"}
: ${find_bin:="find"}
: ${sed_bin:="sed"}
: ${date_bin:="date"}
: ${expr_bin:="expr"}
: ${lvm_bin:="lvm"}
: ${fsck_bin:="fsck"}
: ${mkfs_bin:="mkfs"}
: ${resize2fs_bin:="resize2fs"}
: ${bc_bin:="bc"}
: ${wc_bin:="wc"}
: ${printf_bin:="printf"}
: ${mailx_heirloom_bin:="heirloom-mailx"}
: ${mailx_bsd_bin:="bsd-mailx"}
: ${mutt_bin:="mutt"}
: ${snail_bin:="s-nail"}
: ${uuencode_bin:="uuencode"}
: ${udevadm_bin:="udevadm"}
: ${readlink_bin:="readlink"}
: ${sispmctl_bin:="sispmctl"}
: ${hostname_bin:="hostname"}
: ${partimage_bin:="partimage"}
: ${partclone_bin:="partclone.fat"}
: ${ntfsclone_bin:="ntfsclone"}
: ${dd_bin:="dd"}
: ${xen_bin:="xl"}
: ${kpartx_bin:="kpartx"}
: ${partx_bin:="partx"}
: ${ntfs3g_bin:="ntfs-3g"}
: ${ntfsfix_bin:="ntfsfix"}
: ${df_bin:="df"}
: ${nice_bin:="nice"}
: ${ionice_bin:="ionice"}
: ${xargs_bin:="xargs"}


declare -a backups
cf=""
tf=""
find_expr=""
fn=""
lv=""
declare -a rsync_ign_msg_lst
mail_att=""
declare -i nospace_flag=0
declare -i fail_flag=0
declare -a log_lst
declare -a err_lst
declare -a prc_lst
declare -a pid_lst
declare -i used_threads=0
declare -i thread_count=0
date_str="$(${date_bin} +%F_%H-%M-%S)"
dstr="sILLywALKs"
declare -i bu_start_time=0
declare -A src_path
declare -A dst_path
declare -A src_lvs
declare -A cl_opts
declare -A gf_opts
declare -A cb_opts
declare -A opts
declare -A df_opts=(
	['debug']=0
	['debug_l']=3
	['debug_m']=2
	['conf_path']="${conf_path}"
	['config']="${conf_path}/global.conf"
	['confd']="${conf_path}/conf.d"
	['bu_lang']="/usr/share/rsync-backup/rsb_lang_en"
	['lib_base']="${lib_path}"
	['lib_path']="${lib_path}"
	['log_path']="${log_path}"
	['log_keep']=30
	['log_compress']=1
	['dry_run']=""
	['mail_subject']="[rsync-backup] from localhost.local"
	['operator_mail']=""
	['from_mail']=""
	['smtp']=""
	['smtp-auth']=""
	['smtp-auth-user']=""
	['smtp-auth-password']=""
	['smtp-use-starttls']="1"
	['sendwait']=1
	['off_days']="Sat Sun"
	['rsync_flags']="--archive --compress --numeric-ids --delete --protect-args"
	['rsync_single_flags']=""
	['rsync_thread_flags']=""
	['method']="rsync"
	['split_depth']=1
	['maxthreads']=1
	['rsync_ign_errors']="24"
	['rsync_ign_msg']="${conf_path}/ignore.list"
	['snap_size']=""
	['snap_suffix']="_backupsnap"
	['snap_fsck']=1
	['bu_set']=0
	['bu_sets']=""
	['proc_suffix']="_processing"
	['inc_suffix']="_incomplete"
	['last_suffix']="_last"
	['root_name']="sysroot"
	['src_mnt_base']="/mnt/rsync-backup/src"
	['src_mnt_opts']=""
	['src_fs_type']=""
	['src_df_warn']="0"
	['dst_mnt_base']="/mnt/rsync-backup/dst"
	['dst_mnt_opts']=""
	['dst_fs_type']=""
	['dst_fs_opts']=""
	['dst_df_warn']="0"
	['rm_mnt_opts']="stripe"
	['lv_prefix']="<hostname>_"
	['lv_suffix']="_backup"
	['lv_oversize']=1
	['lv_stripes']=1
	['lv_resize']=1
	['lv_src_act']=0
	['lv_src_al']=0
	['lv_src_ae']=0
	['lv_dst_act']=1
	['lv_dst_al']=0
	['lv_dst_ae']=0
	['force_lv_prefix']=0
	['force_lv_suffix']=0
	['skip_unmounted_src']=1
	['skip_mounted_dst']=1
	['warn_inact_lv_src']=1
	['name_rule']=0
	['add_hname']=1
	['add_rpath']=1
	['usb_dev']=""
	['usb_podev']=""
	['usb_posoc']=""
	['usb_uhost']=""
	['usb_pm']=0
	['usb_global']=1
	['usb_pm_timeout']=20
	['usb_dis_tries']=12
	['usb_dis_sleep']=5
	['print_lang']="en_US.utf8"
	['xen_dom']=""
	['xen_dom_templ']="/etc/xen/<xdom-name>.cfg"
	['xen_timeout']=300
	['xen_force_start']=0
	['xen_acpi_shd']=0
	['eval_list_file']=""
	['nclone_flags']=""
	['pclone_flags']=""
	['lz4_flags']="-B4 -1"
	['buffer_flags']="-m 8m"
	['disk2disk']=0
	['clone_disk_label_size']=""
	['rmthreads']=8
	['rmsplit_depth']=2
	)
declare -a h_opts_lst=(
	"conf_path" "lib_base" "lib_path" "sendwait"
	)
declare -a e_opts_lst=(
	"bu_lang" "config" "debug" "debug_l" "debug_m"
	)
declare -a g_opts_lst=(
	"config" "bu_lang" "confd" "rsync_ign_msg" "log_keep" "debug" "debug_l"
	"debug_m" "dry_run" "off_days" "rsync_flags" "rsync_ign_errors"
	"rsync_single_flags" "rsync_thread_flags" "method" "split_depth" "maxthreads"
	"snap_size" "snap_suffix" "snap_fsck" "bu_set" "bu_sets" "mail_subject"
	"operator_mail" "proc_suffix" "inc_suffix" "src_mnt_base" "src_mnt_opts"
	"dst_mnt_base" "dst_mnt_opts" "dst_fs_type" "dst_fs_opts" "rm_mnt_opts"
	"lv_suffix" "lv_prefix" "lv_oversize" "lv_stripes" "lv_resize"
	"lv_src_act" "lv_src_al" "lv_src_ae" "lv_dst_act" "lv_dst_al"
	"lv_dst_ae" "force_lv_prefix" "force_lv_suffix" "skip_unmounted_src"
	"skip_mounted_dst" "warn_inact_lv_src" "root_name" "name_rule"
	"add_hname" "add_rpath" "destination" "last_suffix" "dst_relpath"
	"usb_dev" "usb_podev" "usb_posoc" "usb_uhost" "usb_pm"
	"usb_global" "usb_pm_timeout" "usb_dis_tries" "usb_dis_sleep"
	"from_mail" "smtp" "smtp-auth" "smtp-auth-user" "smtp-auth-password"
	"smtp-use-starttls" "log_path" "depth" "log_compress" "print_lang"
	"xen_dom_templ" "xen_timeout" "xen_force_start" "xen_acpi_shd"
	"eval_list_file" "nclone_flags" "pclone_flags" "lz4_flags" "buffer_flags"
	"disk2disk" "src_fs_type" "src_df_warn" "dst_df_warn" "disk_label_size"
	"rmthreads" "rmsplit_depth"
	)
declare -a b_opts_lst=(
	"source" "destination" "name" "src_relpath" "dst_relpath" "excludes"
	"snap_fsck" "bu_set" "bu_sets" "snap_size" "rsync_flags" "method"
	"rsync_single_flags" "rsync_thread_flags" "method" "split_depth" "maxthreads"
	"src_mnt_base" "src_mnt_opts" "dst_mnt_base" "dst_mnt_opts"
	"dst_fs_type" "dst_fs_opts" "rm_mnt_opts" "skip_unmounted_src"
	"skip_mounted_dst" "warn_inact_lv_src" "name_rule" "add_hname"
	"add_rpath" "lv_suffix" "lv_prefix" "lv_oversize" "lv_stripes"
	"lv_resize" "lv_src_act" "lv_src_al" "lv_src_ae" "lv_dst_act"
	"lv_dst_al" "lv_dst_ae" "force_lv_prefix" "force_lv_suffix"
	"last_suffix" "usb_dev" "usb_podev" "usb_posoc"	"usb_uhost"
	"usb_pm" "xen_dom" "xen_dom_templ" "xen_timeout"
	"xen_force_start" "xen_acpi_shd" "eval_list_file" "nclone_flags"
	"pclone_flags" "lz4_flags" "buffer_flags" "disk2disk" "src_fs_type"
	"src_df_warn" "dst_df_warn" "disk_label_size" "rmthreads"
	"rmsplit_depth"
	)
declare -a n_opts_lst=(
	"dry_run" "off_days" "rsync_flags" "rsync_single_flags" "rsync_thread_flags"
	"rsync_ign_errors" "rsync_ign_msg" "snap_size" "bu_sets" "operator_mail"
	"src_mnt_opts" "dst_mnt_opts" "dst_fs_type" "dst_fs_opts" "lv_prefix"
	"last_suffix" "eval_list_file" "nclone_flags" "pclone_flags" "src_fs_type"
	"disk_label_size"
	)
declare -a bool_lst=( "lv_resize" "lv_src_act" "lv_src_al" "lv_src_ae"
	"lv_dst_act" "lv_dst_al" "lv_dst_ae" "force_lv_prefix"
	"force_lv_suffix" "skip_unmounted_src" "skip_mounted_dst"
	"warn_inact_lv_src" "add_hname" "add_rpath" "snap_fsck" "usb_pm"
	"usb_global" "smtp-use-starttls" "sendwait" "log_compress"
	"xen_force_start" "xen_acpi_shd" "disk2disk"
	)
declare -a tmpfiles=( "mail_log" "rs_log" "rs_elog" "tmp_file1" "tmp_file2" )
declare -a method_lst=( "rsync" "partimage" "partclone" "ntfsclone" )
declare -a lvs_lst=( "group" "volume" "vsize" "gsize" "gfree" "vattr" )
declare -a mnt_lst=( "source" "target" "fstype" "fsopts" )
declare -a fs_types=( "ext2" "ext3" "ext4" )
declare -a smtp_lst=(
	"smtp" "smtp-auth" "smtp-auth-user" "smtp-auth-password"
	"smtp-use-starttls" "sendwait"
	)
declare -a pwd_lst=(
	"smtp-auth-password"
	)
declare -a clone_methods=( "partimage" "partclone" "ntfsclone" )
declare -a base_bin_lst=(
	"which" "mv" "rm" "mkdir" "rmdir" "test" "gzip" "cut" "cat" "zcat"
	"bc" "df"
	"logger" "touch" "mktemp"
	"find;--version;GNU"
	"grep;--version;GNU"
	"sed;--version;GNU"
	"expr;--version;GNU" 
	"date;--version;GNU"
	)
declare -a bl_bin_lst=(
	"rsync" "mount" "umount" "file" "ln" "hostname" "tail" "head" "wc"
	"printf" "ps"
	)
declare -a br_bin_lst=(
	"${bl_bin_lst[@]}"
	"which" "find" "test" "rm" "mv" "mkdir" "rmdir" "touch"
	)
declare -a lvm_bin_lst=(
	"lvm" "fsck" "mkfs" "resize2fs"
	)
declare -a md5_bin_lst=(
	"md5sum;--version;GNU" "md5"
	)
declare -a ssh_bin_lst=(
	"ssh"
	)
declare -a mnt_bin_lst=(
	"mount;--version;util-linux"
	)
declare -a mntpt_bin_lst=(
	"mountpoint"
	)
declare -a fmnt_bin_lst=(
	"findmnt"
	)
declare -a mailer_bin_lst=(
	"mailx_heirloom;-V;;;;heirloom"
	"snail;-V"
	"mutt;-v"
	"mailx_bsd"
	)
declare -a uuec_bin_lst=(
	"uuencode"
	)
declare -a incr_bin_lst=(
	"find;--version;GNU"
	)
declare -a incl_bin_lst=(
	"sort"
	)
declare -a usb_bin_lst=(
	"udevadm" "readlink"
	)
declare -a po_bin_lst=(
	"sispmctl"
	)
declare -a pimage_bin_lst=(
	"partimage"
	)
declare -a pclone_bin_lst=(
	"partclone"
	)
declare -a nclone_bin_lst=(
	"ntfsclone"
	"dd"
	"lz4"
	"buffer"
	)
declare -a xen_bin_lst=(
	"xen"
	)
declare -a dskutl_bin_lst=(
	"partx"
	"kpartx"
	)
declare -a ntfstools=(
	"ntfs3g"
	"ntfsfix"
	)

rs_stats_re_str="\
Number of files: ([[:digit:],]+)\
( \((reg: ([[:digit:],]+))?(, )?(dir: ([[:digit:],]+))?\
(, )?(link: ([[:digit:],]+))?(, )?(dev: ([[:digit:],]+))?\
(, )?(special: ([[:digit:],]+))?(, [^)]*)?\))?.\
(Number of created files: ([[:digit:],]+)\
( \((reg: ([[:digit:],]+))?(, )?(dir: ([[:digit:],]+))?\
(, )?(link: ([[:digit:],]+))?(, )?(dev: ([[:digit:],]+))?\
(, )?(special: ([[:digit:],]+))?(, [^)]*)?\))?.)?\
(Number of deleted files: ([[:digit:],]+)\
( \((reg: ([[:digit:],]+))?(, )?(dir: ([[:digit:],]+))?\
(, )?(link: ([[:digit:],]+))?(, )?(dev: ([[:digit:],]+))?\
(, )?(special: ([[:digit:],]+))?(, [^)]*)?\))?.)?\
Number of (regular )?files transferred: ([[:digit:],]+).\
Total file size: ([[:digit:],]+) bytes.\
Total transferred file size: ([[:digit:],]+) bytes.\
Literal data: ([[:digit:],]+) bytes.\
Matched data: ([[:digit:],]+) bytes.\
File list size: ([[:digit:],]+).\
File list generation time: ([[:digit:],.]+) seconds.\
File list transfer time: ([[:digit:],.]+) seconds.\
Total bytes sent: ([[:digit:],]+).\
Total bytes received: ([[:digit:],]+).\
.* ([[:digit:],.]+) bytes/sec"

declare -A rs_stats

declare -A usbctl_flags=(
	['pppm']="/sys\${var_usbdev}/port/peer/power/pm_qos_no_power_off"
	['ppm']="/sys\${var_usbdev}/port/power/pm_qos_no_power_off"
	['ppc']="/sys\${var_usbdev}/port/power/control"
	['dpc']="/sys\${var_usbdev}/power/control"
	['dpp']="/sys\${var_usbdev}/power/persist"
	['dpd']="/sys\${var_usbdev}/power/autosuspend_delay_ms"
	['dps']="/sys\${var_usbdev}/power/runtime_status"
	['pps']="/sys\${var_usbdev}/port/power/runtime_status"
	['drvub']="/sys/bus/usb/drivers/usb/unbind"
	['drvb']="/sys/bus/usb/drivers/usb/bind"
	)


# function definitions

	mkTemp() {

		tmp_file=$( ${mktemp_bin} -q /tmp/${0##*/}.XXXXXX)
		if [ ${?} -ne 0 ]; then
			logCmd 1 "unable to create temp file"
			myExit 1
		fi
		logCmd 4 "created temp file: '${tmp_file}'"
		echo ${tmp_file}
	}


	logCmd() {

		local -a level_str=(
			""
			"***_error_***"
			"***_warn_***"
			"***_info_***"
			"***_debug_***"
			"***_devel_***"
			)
		local log_level=${1}
		#local log_str="${level_str[${log_level}]} ${2//+([[:space:]])/ }"
		local log_str="${level_str[${log_level}]} ${2}"
		local att_file=${3}

		[ ${opts['debug']} -ge ${log_level} ] && echo "${log_str}" >/dev/stderr
		[ ${opts['debug_l']} -ge ${log_level} ] && ${logger_bin} -t rsync-backup "${log_str}"
		[ ${opts['debug_m']} -ge ${log_level} ] && [ -e "${mail_log}" ] && echo "${log_str}" >> "${mail_log}"
		if [ -n "${att_file}" ]; then
			[ -n "${mail_att}" ] && mail_att+=" "
			mail_att+="${att_file}"
		fi

		return 0
	}


	verComp() {

		local var_cn=0
		local -a var_v1
		local -a var_v2

		if [[ $1 == $2 ]]; then
			return 1
		fi

		var_v1=( ${1//./ } )
		var_v2=( ${2//./ } )

		# fill empty fields in var_v1 with zeros
		for ((var_cn=${#var_v1[@]} ; var_cn < ${#var_v2[@]} ; var_cn++)); do
			var_v1[${var_cn}]=0
		done

		for ((var_cn=0 ; var_cn < ${#var_v1[@]} ; var_cn++)); do

			if [[ -z ${var_v2[${var_cn}]} ]]; then
				# fill empty fields in var_v2 with zeros
				var_v2[${var_cn}]=0
			fi

			if ((10#${var_v1[${var_cn}]} > 10#${var_v2[${var_cn}]})); then
				return 2
			fi

			if ((10#${var_v1[${var_cn}]} < 10#${var_v2[${var_cn}]})); then
				return 0
			fi
		done

		return 1
	}


	#
	# binary list fields: 'bin-name;get-version-opt;privider;min-version;max-version;rovider-hint'
	#
	checkBin() {

		local var_blst=( ${1} )
		local var_ussh="${2}"
		local var_remote="${2:+${2//@(* |*@|:*)}:}"
		local var_findbin=${3:-0}
		local var_noerr=${4:-0}
		local var_bfull=""
		local -a var_bin
		local -a var_vqlst
		local var_vq=""
		local -a var_v
		local var_vn=""
		local -i var_vfail=1
		local var_tmp=""
		local re_expr1='\<([.[:digit:]]+)\>'
		local re_expr2=""
		local IFS_save="${IFS}"
		local var_bin_exp=""
		local var_bin_full=""
		local -i var_err=1

		if [ ${var_findbin} -ne 0 ] || [ ${var_noerr} -ne 0 ]; then
			var_err=4
		fi

		for var_bfull in "${var_blst[@]}"; do

			IFS=';'
			var_bin=( ${var_bfull} )
			IFS="${IFS_save}"

			eval var_bin_exp=\$\{${var_bin[0]}_bin\}

			logCmd 4 "${str_bin_t} '${var_remote}${var_bin[0]}'"

			if ! var_bin_full=$( ${var_ussh:-eval} "${which_bin} ${var_bin_exp} 2>/dev/null" ); then

				logCmd ${var_err} "${str_bin_m} '${var_remote}${var_bin[0]}'"
				[ ${var_findbin} -eq 0 ] && return 1
				continue
			fi

			logCmd 4 "${str_bin_p} '${var_remote}${var_bin_full}'"

			# end here, if we just check the binary existence
			if [ -z "${var_bin[1]}" ]; then 

				[ ${var_findbin} -eq 0 ] && continue
				echo "${var_bin[0]}"
				return 0
			fi

			var_vqlst=( ${var_bin[1]//,/ } )
			var_vfail=1

			# try getting version info
			var_v=()
			for var_vq in "${var_vqlst[@]}"; do

				IFS=$'\x0a'
				var_v=( $( IFS="${IFS_save}"; ${var_ussh:-eval} "${var_bin_exp} ${var_vq}" ) )
				IFS="${IFS_save}"
				if [ ${?} -eq 0 ]; then
					var_vfail=0
					break
				fi
			done

			var_tmp=${var_bin[5]:+5}
			var_tmp=${var_tmp:-2}
			var_tmp="${var_bin[${var_tmp}]:+ : ${var_bin[${var_tmp}]}}"

			if [ ${var_vfail} -ne 0 ]; then

				logCmd ${var_err} "${str_bin_prf} '${var_remote}${var_bin[0]}${var_tmp}'"
				[ ${var_findbin} -eq 0 ] && return 1
				continue
			fi

			if [ -n "${var_bin[2]}" ]; then

				re_expr2="\<${var_bin[2]}\>"
				if ! [[ "${var_v[0]}" =~ ${re_expr2} ]]; then

					logCmd ${var_err} "${str_bin_prf} '${var_remote}${var_bin[0]}${var_tmp}'"
					[ ${var_findbin} -eq 0 ] && return 1
					continue
				fi

			fi

			logCmd 4 "${str_bin_prs} '${var_remote}${var_bin[0]}${var_tmp}'"

			# do not continue if we do not check the binary version
			if [ -z "${var_bin[3]}" ] && [ -z "${var_bin[4]}" ]; then

				[ ${var_findbin} -eq 0 ] && continue
				echo "${var_bin[0]}"
				return 0
			fi

			# fail if we can't get the binary version
			if ! [[ "${var_v[0]}" =~ ${re_expr1} ]]; then

				logCmd ${var_err} "${str_bin_vf} '${var_remote}${var_bin[0]}'"
				[ ${var_findbin} -eq 0 ] && return 1
				continue
			fi

			var_vn=${BASH_REMATCH[1]}

			# match minimum version number
			if [ -n "${var_bin[3]}" ]; then
				verComp "${var_bin[3]}" "${var_vn}"

				if [ ${?} -gt 1 ]; then
					logCmd ${var_err} "${str_bin_vrf} '${var_remote}${var_bin[0]} : ${var_bin[3]}(min) > ${var_vn}(curr)'"
					[ ${var_findbin} -eq 0 ] && return 1
					continue
				fi
			fi	

			# match max version number
			if [ -n "${var_bin[4]}" ]; then
				verComp "${var_bin[4]}" "${var_vn}"

				if [ ${?} -lt 1 ]; then
					logCmd ${var_err} "${str_bin_vrf} '${var_remote}${var_bin[0]} : ${var_vn}(curr) > ${var_bin[4]}(max)'"
					[ ${var_findbin} -eq 0 ] && return 1
					continue
				fi
			fi

			var_tmp="${var_bin[0]} : ${var_bin[3]:+${var_bin[3]}(min) <= }${var_vn}(curr)${var_bin[4]:+ <= ${var_bin[4]}(max)}"
			logCmd 4 "${str_bin_vrs} '${var_remote}${var_tmp}'"
			if [ ${var_findbin} -ne 0 ]; then
				echo "${var_bin[0]}"
				return 0
			fi
		done

		[ ${var_findbin} -eq 0 ] && return 0

		logCmd 1 "${str_bin_nl} ${var_remote}${var_blst[*]}"
		return 1
	}


	# tests a list of binaries and saves their state
	# options: binary class name - for reference purpose
	#          binary list - list of semicolon strings, see checkBin for fields
	#          ssh string - for remote host bin check (can be nullstring)
	#          find binary (optional) - suppress errors and return found binary 0|1
	#          noerror (optional) - just suppress errors, only return state 0|1
	# returns: 0|1, opt. the found binary
	checkBinSave() {

		local var_blname="${1}"
		local var_blst="${2}"
		local var_ussh="${3}"
		local var_findbin=${4:-0}
		local var_noerr=${5:-0}
		#local var_host="${var_ussh/#@(* |*@)}"
		local var_host="${var_ussh//@(* |*@|:*)}"
		: ${var_host:="localhost"}
		local var_bspath="${opts['lib_base']}/bs"
		local var_bsfile="${var_bspath}/${var_host}"
		local var_tmp=""

		if [ "${var_blname}" == "base" ] && [ -e "${var_bsfile}" ] || \
		   var_tmp="$( ${grep_bin} -m1 "${var_blname}" \
		   "${var_bsfile}" 2>/dev/null )"; then

			logCmd 4 "${str_bin_tap} ${var_host}:${var_blname}"
			var_tmp="${var_tmp#* }"
		else
			if ! var_tmp=$( checkBin "${var_blst}" "${var_ussh}" \
			        ${var_findbin} ${var_noerr} ); then

				[ ${var_noerr} -eq 0 ] && return 1
				var_tmp="not_available"
			fi

			pathCreate "${var_bspath}" || return 0

			if echo "${var_blname}${var_tmp:+ ${var_tmp}}" >> "${var_bsfile}"; then
				logCmd 4 "${str_bin_css} ${var_host}:${var_blname}"
			else
				logCmd 1 "${str_bin_chse} ${var_host}:${var_blname}"
			fi
		fi

		[ ${var_findbin} -eq 1 ] && echo "${var_tmp}"
		[ "${var_tmp}" = "not_available" ] && return 1

		return 0
	}


	encodeAtt() {

		local var_att_lst=( ${1} )
		local var_att=""

		for var_att in "${var_att_lst[@]}"; do

			${uuencode_bin} "${var_att}" "${var_att##*/}"
			[ ${?} -ne 0 ] && logCmd 1 "${str_mail_uuec_e} '${var_att}'"
		done
	}


	myExit() {

		local ecode=${1}
		local var_mailsub="${opts['mail_subject']}"
		local var_err=""
		local log_str=""
		local lockfile="${opts['lib_path']}/lockfile"
		local var_log=""
		local fn=""
		local var_from=""
		local -a var_att
		local -a var_smtp
		local -i rc
		local var_pccln='[[:space:]]*[.[:digit:]]{3,6} percent completed'
		local var_mailer=""
		local var_mailer_bin=""

		logCmd 3 "${str_bu_end}"

		if [ ${ecode} -gt 0 ] && [ ${ecode} -lt 240 ]; then

			if [ "${opts['method']}" == "rsync" ]; then
				# kill still running rsync threads
				waitRsyncThreads 1
			else
				# clean log from percentage count
				${grep_bin} -v -E "${var_pccln}" ${tmp_file1} >> ${rs_log}
			fi

			# care about the log stuff
			handleRsyncLogs
		fi

		# only try to send email if we have something in mail-log and an operator email
		if [ -s "${mail_log}" ] && [ -n "${opts['operator_mail']}" ]; then

			# read log file into a single string var
			log_str=$( < "${mail_log}" )

			# decide email subject as success, warning or error
			if [[ "${log_str}" =~ _error_ ]]; then
				var_mailsub+="${str_mail_sub_e}"
			elif [[ "${log_str}" =~ _warn_ ]]; then
				var_mailsub+="${str_mail_sub_w}"
			else
				var_mailsub+="${str_mail_sub_s}"
			fi

			# bin check for mail bin ...
			var_mailer="$( checkBinSave "mail" "${mailer_bin_lst[*]}" "" 1 )"

			logCmd 4 "${str_mail_bin} ${var_mailer:-none}"

			case "${var_mailer}" in

				mutt)

					# append rsync error log-file as attachment
					[ -n "${mail_att}" ] && mail_att="-a ${mail_att} --"

					var_err=$( echo "${log_str}" | ${mutt_bin} -x -s "${var_mailsub}" \
					           ${mail_att} ${opts['operator_mail']} 2>&1 >/dev/null )

					rc=${?}
				;;

				mailx_heirloom|snail)

					# append rsync error log-file as attachment
					if [ -n "${mail_att}" ]; then

						for fn in ${mail_att}; do
							var_att+=( "-a" "${fn}" )
						done
					fi

					# use 'from' if provided
					if [ -n "${opts['from_mail']}" ]; then
						var_from="-r ${opts['from_mail']}"
					fi

					# use smtp connection options if provided
					if [ -n "${opts['smtp']}" ]; then

						for fn in "${smtp_lst[@]}"; do

							[ -z "${opts[${fn}]}" ] && continue

							if containsElement "${fn}" "${bool_lst[@]}" && \
							   [ ${opts[${fn}]} -eq 1 ]; then

								var_smtp+=( "-S" "${fn}" )
							else
								var_smtp+=( "-S" "${fn}=${opts[${fn}]}" )
							fi
						done
					fi

					eval var_mailer_bin=\$\{${var_mailer}_bin\}
					var_err=$( echo "${log_str}" | ${var_mailer_bin} \
					           -s "${var_mailsub}" ${var_from} ${var_smtp[@]} \
					           ${var_att[@]} ${opts['operator_mail']} 2>&1 >/dev/null )

					rc=${?}
				;;

				mailx_bsd)

					if checkBinSave "uuencode" "${uuec_bin_lst[*]}"; then

						var_err=$( (echo "${log_str}"; encodeAtt "${mail_att}") | \
						           ${mailx_bsd_bin} -s "${var_mailsub}" \
						           ${opts['operator_mail']} 2>&1 >/dev/null )

						rc=${?}
					else
						var_err="${str_uuencode_bin_na}"
						rc=1
					fi
				;;

				*)
					var_err="${str_mail_bin_na}"
					rc=1
				;;
			esac

			if [ ${rc} -eq 0 ]; then
				logCmd 4 "${str_mail_send_s} ${opts['operator_mail']}"
			else
				logCmd 1 "${str_mail_send_e} '${var_err}'"
			fi
		fi

		# purge rsync thread queue
		setRsyncThreads 0

		# remove residual temp files
		for fn in "${tmpfiles[@]}"; do

			eval "var_log=\${${fn}}"
			[ -z "${var_log}" ] && continue
			[ -e "${var_log}" ] || continue
			Remove "${var_log}" "" 1
		done

		# exit without rm lockfile if we just checked for a running
		# backup job, or if lockfile isn't created jet.
		[ ${ecode} -lt 250 ] && Remove "${lockfile}" "" 1
		exit ${ecode}
	}


	# this function is executed if the scrip recives an ctrl-c or kill -15
	sigHandler() {

		logCmd 2 "${str_bu_term}"
		Cleanup
		usbStop
		myExit 3
	}


	# test if an array contains a certain element
	# options: element, array in form "${array[@]}"
	# return: 0 = element found, 1 = element not found
	containsElement() {

		local fn

		for fn in "${@:2}"; do
			[ "${fn}" == "${1}" ] && return 0
		done

		return 1
	}


	# evaluates the command line options into an assoziative array
	# options: argument list
	# provides: cl_opts
	# uses: g_opts_lst
	evalArgs() {

		local var_arg=""
		local var_key=""
		local var_val=""

		[ $# -eq 0 ] && return 0
		logCmd 4 "${str_opt_pclo}"

		for var_arg in "${@}"; do

			if ${expr_bin} 'index' "${var_arg}" '=' '>' '1' >/dev/null; then

				var_key="${var_arg/%=*}"
				var_val="${var_arg/#*=}"

				if containsElement "${var_key}" "${g_opts_lst[@]}"; then
					eval "cl_opts['${var_key}']='${var_val}'"
				else
					logCmd 2 "${str_opt_iu} '${var_key}=${var_val}'"
				fi

			elif ! ${expr_bin} 'index' "${var_arg}" '=' >/dev/null; then
				command="${var_arg}"
			fi
		done
	}


	# evaluates a configuration file into an assoziative array
	# options: config file path, array name, options list*
	readConfFile() {

		local var_file="${1}"
		local var_name="${2}"
		local -a var_opts_lst=( ${@:3} )
		local var_key=""
		local var_val=""
		local var_l=""
		local -a var_opts
		local re_expr="^([_[:alnum:]-]+)[[:space:]]*=[[:space:]]*([[:print:]]+?)$"

		if ! [ -e "${var_file}" ]; then
			logCmd 1 "${str_opt_cf_ne} '${var_file}'"
			return 1
		fi
		logCmd 4 "${str_opt_pcf} '${var_file}'"

		while read var_l; do
			var_opts+=( "${var_l}" )
		done < "${var_file}"

		for var_l in "${var_opts[@]}"; do

			# strip and ignore comments
			var_l="${var_l/%#*}"
			[ -z "${var_l}" ] && continue

			# remove quotations
			var_l="${var_l//[$'\x22\x27']}"
			# remove leading and trailing spaces
			var_l="${var_l/#*([[:space:]])}"
			var_l="${var_l/%*([[:space:]])}"

			# split line in key and value
			if ! [[ "${var_l}" =~ ${re_expr} ]]; then
				logCmd 1 "${str_opt_cf_pe} '${var_file}'"
				return 1
			fi

			var_key="${BASH_REMATCH[1]}"
			var_val="${BASH_REMATCH[2]}"

			# create/set assoziative array element var_name[var_key]=var_val 
			if containsElement "${var_key}" "${var_opts_lst[@]}"; then
				eval "${var_name}['${var_key}']='${var_val}'"
			else
				logCmd 2 "${str_opt_iu} '${var_key}=${var_val}'"
			fi
		done

		return 0
	}


	# used to set options in the priority order:
	#   cmd line, backup config, global config, defaults
	# options: list of options to set
	# provides: div options
	# uses: cl_opts, cb_opts, gf_opts, df_opts, n_opts_lst
	setOptions() {

		local fn

		for fn in "${@}"; do

			opts[${fn}]="${cl_opts[${fn}]:-${cb_opts[${fn}]:-${gf_opts[${fn}]:-${df_opts[${fn}]}}}}"

			# test if the option can have a nullstring
			if ! containsElement "${fn}" "${n_opts_lst[@]}"; then
				continue
			fi

			# test if we have to 0-set the option
			if ( [ -z "${cl_opts[${fn}]}" ] && \
			     [ "${cl_opts[${fn}]+${dstr}}" = "${dstr}" ] ) || \
			   ( [ -z "${cl_opts[${fn}]+${dstr}}" ] && \
			     [ -z "${cb_opts[${fn}]}" ] && \
			     [ "${cb_opts[${fn}]+${dstr}}" = "${dstr}" ] ) || \
			   ( [ -z "${cl_opts[${fn}]+${dstr}}" ] && \
			     [ -z "${cb_opts[${fn}]+${dstr}}" ] && \
			     [ -z "${gf_opts[${fn}]}" ] && \
			     [ "${gf_opts[${fn}]+${dstr}}" = "${dstr}" ] ); then

				logCmd 4 "${str_opt_nv} '${fn}'"
				opts[${fn}]=""
			fi
		done
	}


	loadLang() {

		if [ -e "${1}" ]; then
			source "${1}"
		else
			logCmd 1 "${str_opt_sf_e} ${1}"
			return 1
		fi

		return 0
	}


	# opts: $1 path to remove
	#       $2 ssh string, for remote operation
	#       $3 0|1 remove, event if dryrun is set
	#       $4 recursive remove
	Remove() {

		local var_path="${1}"
		local var_ussh="${2}"
		local var_nodryrun="${3:-0}"
		local var_rec="${4:-0}"
		local var_err=""
		local -i var_rmthreads=${opts['rmthreads']}
		local -i var_rmsplit_depth=${opts['rmsplit_depth']}
		local var_rm_cmd="${rm_bin} -f ${var_path}"

		# TODO: Sanity check: try not do delete symlink or singel file this way
		if [ ${var_rec} -eq 1 ]; then
			var_rm_cmd="${ionice_bin} -c 3 ${nice_bin} ${find_bin} ${var_path} "
			var_rm_cmd+="-maxdepth ${var_rmsplit_depth} -depth -type d -print0 | "
			var_rm_cmd+="${xargs_bin} -0 -P ${var_rmthreads} -n 1 rm -rf"
		fi

		if [ -z "${var_path}" ] || [ "${var_path}" == "/" ]; then
			logCmd 1 "${str_path_rm_rf} '${var_path}'"
			return 1
		fi

		logCmd 4 "deleting: '${var_path}'"

		if [ -n "${opts['dry_run']}" ] && [ ${var_nodryrun} -eq 0 ]; then

			logCmd 4 "${var_ussh:+${var_ussh} }${var_rm_cmd}"
			return 0
		fi

		if var_err=$( ${var_ussh:-eval} "${var_rm_cmd} 2>&1 >/dev/null" ); then

			logCmd 4 "${str_path_rm_s} '${var_path}'"
		else
			logCmd 1 "${str_path_rm_f} '${var_err}'"
			return 1
		fi

		return 0
	}


	cleanOldLog() {

		local var_path=${1}
		local var_name=${2}
		local var_keep=${3}
		local var_full="${var_path}/${var_name}"
		local var_last="${var_full}${opts['last_suffix']}"
		local -a var_llst=()
		local var_l=""
		local find_expr1="${find_bin} '${var_path}/' -maxdepth 1 "
		find_expr1+="-type f -regextype posix-egrep "
		find_expr1+="-regex '^${var_full}_[0-9]{4}([-|_][0-9]{2}){5}.+$'"
		local find_expr2="${find_bin} '${var_path}/' -maxdepth 1 "
		find_expr2+="-type l -regextype posix-egrep "
		find_expr2+="-regex '^${var_last}.+$'"
		local sed_expr='s,^.+_?([0-9]{4}-[0-9]{2}-[0-9]{2})_([0-9]{2})-([0-9]{2})-([0-9]{2}).+$,\1 \2:\3:\4,'
		local IFS_save="${IFS}"

		[ -z "${var_path}" ] || [ -z "${var_name}" ] || \
		  [ -z "${var_keep}" ] && return 1

		logCmd 4 "${str_log_clean} '${var_name}'"

		# get a sorted list of all logs, matching the current backup
		IFS=$'\x0a'
		var_llst=( $( IFS="${IFS_save}" ; \
			eval "${find_expr1} 2>/dev/null | sort" ) )
		IFS="${IFS_save}"

		if [ ${#var_llst[@]} -eq 0 ]; then
			logCmd 4 "${str_log_clean_nl} '${var_name}'"
			return 0
		fi

		# find and remove all logs older than var_keep (${3}) days
		for var_l in "${var_llst[@]}"; do

			# just in case
			[ -z "${var_l}" ] && continue

			if [ $(${date_bin} --date "$(echo ${var_l} | \
			     ${sed_bin} -r -e "${sed_expr}")" +%s) -le \
			     $(${date_bin} --date "${var_keep} days ago" +%s) ]; then

				Remove "${var_l}"
			fi
		done

		# get a list of all 'last log' links
		IFS=$'\x0a'
		var_llst=( $( IFS="${IFS_save}" ; \
			eval "${find_expr2}" 2>/dev/null ) )
		IFS="${IFS_save}"

		if [ ${#var_llst[@]} -eq 0 ]; then
			return 0
		fi

		# find and remove 'last log' links without target
		for var_l in "${var_llst[@]}"; do

			# just in case
			[ -z "${var_l}" ] && continue

			[ -e "${var_l}" ] || Remove "${var_l}"
		done

		return 0
	}


	# uses: tmp_file1, tmp_file2, dst_path[target,ussh]
	checkErrLog() {

		local var_log="${1}"
		local var_mnt="${dst_path['target']}"
		local var_ussh="${dst_path['ussh']}"
		local fn=""
		local var_nospace=""

		[ -z "${var_log}" ] || [ -z "${var_mnt}" ] && return 1

		# get error log without the first/empty line(s)
		${tail_bin} -n +2 "${var_log}" | \
			${grep_bin} -v -E '^[[:space:]]*$' > "${tmp_file1}"

		# do nothing further, if the rest is empty
		[ -s "${tmp_file1}" ] || return 0

		logCmd 4 "${str_log_err_chk}"

		# test for "No space left on device"
		if var_nospace=$( ${grep_bin} 'No space left on device' "${tmp_file1}" ); then

			if ! [[ ${var_nospace} =~ lsetxattr ]]; then

				if ! ${var_ussh:-eval} "${touch_bin} '${var_mnt}/nospace_lock'"; then
					logCmd 1 "${str_log_err_cens}: '${var_mnt}'"
				fi
				nospace_flag=1
				logCmd 1 "${str_log_err_ns}"
				return 2
			fi
		fi

		# test if we can ignore everything in error log
		for fn in "${!rsync_ign_msg_lst[@]}"; do

			logCmd 4 "${str_log_ign_str} '${rsync_ign_msg_lst[${fn}]}'"
			${grep_bin} -v -E "${rsync_ign_msg_lst[${fn}]}" "${tmp_file1}" > "${tmp_file2}"

			${cat_bin} "${tmp_file2}" > "${tmp_file1}"

			! [ -s "${tmp_file1}" ] && break
		done

		if [ -s "${tmp_file1}" ]; then

			logCmd 2 "${str_log_err_inf}"
			return 2
		fi

		logCmd 4 "${str_log_err_ign}"
		return 1
	}


	# uses: opts[log_compress]
	moveTmpLog() {

		local var_suff=".log"
		local var_csuff=".gz"
		local var_old="${1}"
		local var_new="${2}${var_suff}"
		local var_err=""

		[ -z "${var_old}" ] || [ "${var_new}" == "${var_suff}" ] && return 1

		if ! [ -e "${var_old}" ]; then
			logCmd 1 "${str_tmp_one} '${var_old}'"
			return 1
		fi

		if [ -e "${var_new}" ]; then
			logCmd 1 "${str_tmp_ne} '${var_new}'"
			return 1
		fi

		if ! var_err=$( ${mv_bin} "${var_old}" "${var_new}" 2>&1 >/dev/null );	then
			logCmd 1 "${str_tmp_mv_f} '${var_err}'"
			return 1
		fi

		if [ ${opts['log_compress']} -eq 1 ]; then

			if [ -e "${var_new}${var_csuff}" ]; then
				logCmd 1 "${str_tmp_gze} '${var_new}${var_csuff}'"

			elif var_err=$( ${gzip_bin} "${var_new}" 2>&1 >/dev/null ); then

				var_suff+="${var_csuff}"
				var_new+="${var_csuff}"
			else
				logCmd 1 "${str_tmp_gz_f} '${var_err}'"
			fi
		fi

		logCmd 4 "${str_tmp_mv_s} '${var_new}'"
		echo "${var_suff}"
		return 0
	}


	linkCreate() {

		local var_t="${1}"
		local var_n="${2}"
		local var_ussh="${3}"
		local var_nodryrun=${4:-0}
		local var_err=""

		[ -z "${var_t}" ] || [ -z "${var_n}" ] && return 1

		if [ -n "${opts['dry_run']}" ] && [ ${var_nodryrun} -eq 0 ]; then
			logCmd 4 "${var_ussh:+${var_ussh} }${ln_bin} -sf ${var_t} ${var_n}"
			return 0
		fi

		if ${var_ussh:-eval} "${test_bin} -h '${var_n}'"; then

			Remove "${var_n}" "${var_ussh}" ${var_nodryrun} || return 1
		fi

		if ! var_err=$( ${var_ussh:-eval} "${ln_bin} -s '${var_t}' '${var_n}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_ln_mk_f} '${var_err}'"
			return 1
		fi

		logCmd 4 "${str_ln_mk_s} '${var_n}' -> '${var_t}'"
		return 0
	}


	pathCreate() {

		local var_path="${1}"
		local var_ussh="${2}"
		local var_err=""

		if [ -z "${var_path}" ]; then
			logCmd 1 "${str_path_md_r}"
			return 1
		fi

		if ${var_ussh:-eval} "${test_bin} -d '${var_path}'"; then
			return
		fi

		if ! var_err=$( ${var_ussh:-eval} "${mkdir_bin} -p '${var_path}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_path_md_f} '${var_err}'"
			return 1
		fi

		logCmd 4 "${str_path_md_s} '${var_path}'"
		return 0
	}


	pathRename() {

		local var_old="${1}"
		local var_new="${2}"
		local var_ussh="${3}"
		local var_err=""

		[ -z "${var_old}" ] || [ -z "${var_new}" ] && return 1

		if [ -n "${opts['dry_run']}" ]; then
			logCmd 4 "${var_ussh:+${var_ussh} }${mv_bin} ${var_old} ${var_new}"
			return 0
		fi

		if ! ${var_ussh:-eval} "${test_bin} -e '${var_old}'"; then
			logCmd 1 "${str_path_mv_src_e} '${var_old}'"
			return 1
		fi

		if ${var_ussh:-eval} "${test_bin} -e '${var_new}'"; then
			logCmd 1 "${str_path_mv_tar_e} '${var_new}'"
			return 1
		fi

		if ! var_err=$( ${var_ussh:-eval} "${mv_bin} '${var_old}' '${var_new}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_path_mv_f} '${var_err}''"
			return 1
		fi

		logCmd 4 "${str_path_mv_s} '${var_old}' -> '${var_new}'"
		return 0
	}


	pathRemove() {

		local var_path="${1}"
		local var_ussh="${2}"
		local var_err=""

		[ -z "${var_path}" ] && return 1

		if ! ${var_ussh:-eval} "${test_bin} -d '${var_path}'"; then
			logCmd 4 "${str_path_rd_ne} '${var_path}'"
			return 0
		fi

		if ! var_err=$( ${var_ussh:-eval} "${rmdir_bin} '${var_path}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_path_rd_f} '${var_err}'"
			return 1
		fi

		logCmd 4 "${str_path_rd_s} '${var_path}'"
		return 0
	}


	# uses: opts[snap_size, snap_suffix]
	isSnapshot() {

		local var_lv="${1}"
		local var_ussh="${2}"
		local lvs_opt="--options lv_attr --noheadings --nosuffix --unbuffered"
		local lvs_out=""

		[ -z "${var_lv}" ] && return 1

		# simple test via snap_size option and device suffix
		if ! ( [ -n "${opts['snap_size']}" ] && \
		   [[ "${var_lv}" =~ ^.+${opts['snap_suffix']}$ ]] ); then

			return 1
		fi

		lvs_out=$( ${var_ussh:-eval} "${lvm_bin} lvs \
			${lvs_opt} '${var_lv}' 2>/dev/null" )

		# warn if snapshot is invalidated
		if [[ "${lvs_out}" =~ ^[[:space:]]*S...I ]]; then
			logCmd 2 "${str_snap_rm_is} '${var_snap}'"
		fi

		[[ "${lvs_out}" =~ ^[[:space:]]*[sS] ]] || \
		[[ "${lvs_out}" =~ ^[[:space:]]*V.....t..k ]] && return 0

		return 1
	}


	snapCreate() {

		local var_lv="${1/%${opts['snap_suffix']}}"
		local var_snap="${1/#*\/}"
		local var_size="-L ${2}"
		local var_ussh="${3}"
		local var_err=""

		[ -z "${var_lv}" ] || [ -z "${var_snap}" ] && return 1

		logCmd 3 "${str_snap_mk} '${var_snap}'"

		if [ "${var_size}" == "-L T" ]; then
			var_size=""
		fi

		if ! var_err=$( ${var_ussh:-eval} "${lvm_bin} lvcreate ${var_size} -s \
		     -n '${var_snap}' '${var_lv}' 2>&1 >/dev/null" ); then

			logCmd 1 "${str_snap_mk_f} '${var_err}'"
			return 1
		fi

		logCmd 4 "${str_snap_mk_s} '${var_snap}'"
		return 0
	}


	snapRemove() {

		local var_snap="${1}"
		local var_ussh="${2}"
		local var_err=""
		local count=0

		[ -z "${var_snap}" ] && return 1

		logCmd 4 "${str_snap_rm} '${var_snap}'"

		for ((count = 1 ; count <= 10 ; count++)); do

			sleep 1
			if var_err=$( ${var_ussh:-eval} "${lvm_bin} lvremove -f '${var_snap}' 2>&1 >/dev/null" ); then
				logCmd 4 "${str_snap_rm_s} '${var_snap}'"
				return 0
			else
				logCmd 4 "${str_snap_rm_r} '${count} : ${var_err}'"
			fi
		done

		# fail if we wheren't able to create the snapshot after 10 tries
		logCmd 1 "${str_snap_rm_f} '${var_snap}'"
		return 1
	}


	lvChange() {

		local var_lv=${1}
		local var_state=${2}
		local var_local=${3:-0}
		local var_excl=${4:-0}
		local var_force=${5:-0}
		local var_ussh="${6}"
		local var_opts="-a"
		local var_err=""

		[ -z "${var_lv}" ] || [ -z "${var_state}" ] && return 1

		if [ ${var_excl} -eq 1 ]; then
			var_opts+="e"
		elif [ ${var_local} -eq 1 ]; then
			var_opts+="l"
		fi
		if [ ${var_state} -eq 1 ]; then
			var_opts+="y"
		else
			var_opts+="n"
		fi
		if [ ${var_force} -eq 1 ]; then
			var_opts+=" -K"
		fi

		logCmd 3 "${str_lvm_ch} '${var_lv} -> ${var_opts}'"

		if ! var_err=$( ${var_ussh:-eval} "${lvm_bin} lvchange ${var_opts} \
		     '${var_lv}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_lvm_ch_f} '${var_err}'"
			return 1
		fi
		logCmd 4 "${str_lvm_ch_s} '${var_lv}' -> ${var_opts}"

		return 0
	}


	lvResize() {

		local var_lv=${1}
		local var_size=${2}
		local var_ussh=${3}
		local var_err=""

		[ -z "${var_lv}" ] || [ -z "${var_size}" ] && return 1

		logCmd 3 "${str_lvm_rs} '${var_lv}'"

		queryMount "s" "${var_lv}" "${var_ussh}"
		if [ ${?} -ne 1 ]; then
			logCmd 1 "${str_fs_dm} '${var_lv}'"
			return 1
		fi

		if ! var_err=$( ${var_ussh:-eval} "${lvm_bin} lvresize -L ${var_size}b \
		     '${var_lv}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_lvm_rs_f} '${var_err}'"
			return 1
		fi
		logCmd 4 "${str_lvm_rs_s} '${var_lv}' (${var_size} bytes)"

		return 0
	}


	lvCreate() {

		local var_name=${1}
		local var_vg=${2}
		local var_size=${3}
		local var_stripes=${4}
		local var_ussh=${5}
		local var_lv="${var_vg}/${var_name}"
		local var_err=""

		if [ -z "${var_name}" ] || [ -z "${var_vg}" ] || \
		   [ -z "${var_size}" ]; then

			logCmd 1 "${str_lvm_mk_z}"
			return 1
		fi

		logCmd 3 "${str_lvm_mk} '${var_lv}'"

		if ! var_err=$( ${var_ussh:-eval} "${lvm_bin} lvcreate -n '${var_name}' \
		     ${var_size} ${var_stripes} '${var_vg}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_lvm_mk_f} '${var_err}'"
			return 1
		fi
		logCmd 4 "${str_lvm_mk_s} '${var_lv}' (${var_size} bytes)"

		return 0
	}


	fsCreate() {

		local var_dev=${1}
		local var_fs=${2:-"ext4"}
		local var_fsopts=${3}
		local var_ussh=${4}
		local var_err=""
		local tmp_data=""

		if ! var_err=$( ${var_ussh:-eval} "${mkfs_bin} -t ${var_fs} \
		     ${var_fsopts} '${var_dev}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_fs_mk_f} '${var_err}'"
			return 1
		fi
		tmp_data="(type: ${var_fs}${var_fsopts:+; opts: ${var_fsopts}})"
		logCmd 4 "${str_fs_mk_s} '${var_dev}' ${tmp_data}"

		return 0
	}


	fsResize() {

		local var_dev=${1}
		local var_ussh=${2}
		local var_err=""

		var_err=$( ${var_ussh:-eval} "${fsck_bin} -fa '${var_dev}' 2>&1 >/dev/null" )
		if (( ${?} & 4 )); then
			logCmd 1 "${str_fs_chk_f} '${var_err}'"
			return 1
		fi
		logCmd 4 "${str_fs_chk_s} '${var_dev}'"

		# TODO:  check for other fs than ext
		if ! var_err=$( ${var_ussh:-eval} "${resize2fs_bin} '${var_dev}' 2>&1 >/dev/null" ); then
			logCmd 1 "${str_fs_rs_f} '${var_err}'"
			return 1
		fi
		logCmd 4 "${str_fs_rs_s} '${var_dev}'"

		return 0
	}


	# create xen domU <name>
	# uses: opts[xen_dom_templ, dry_run]
	# param: <xen domain name>
	#        <ssh connection string>
	# rc: 0 success
	#     1 already running
	#     2 failed to create
	xenCreate() {

		local var_xdom="${1}"
		local var_ussh="${2}"
		local var_xdom_cfg="${opts['xen_dom_templ']/<xdom-name>/${var_xdom}}"
		local var_err=""

		# test for binaries locally or remote
		checkBinSave "xen" "${xen_bin_lst[*]}" "${var_ussh}" || return 1

		if ${xen_bin} list 2>/dev/null | \
		   ${grep_bin} -q -E "^${var_xdom}[[:space:]]+"; then

			logCmd 3 "${str_xen_ar} '${var_xdom}'"
			return 1
		fi

		if ${var_ussh:-eval} "${test_bin} ! -r '${var_xdom_cfg}'"; then
			logCmd 1 "${str_xen_nocfg} '${var_ussh:+${var_ussh} }${var_xdom_cfg}'"
			return 2
		fi

		logCmd 3 "${str_xen_cr} '${var_xdom}'"

		if [ -n "${opts['dry_run']}" ]; then
			logCmd 4 "${var_ussh:+${var_ussh} }${xen_bin} create ${var_xdom_cfg}"
			return 0
		fi

		# we wait a moment before we start domU to give the
		# devices a moment to settle after all actions we did
		sleep 5

		if ! var_err=$( ${var_ussh:-eval} \
		   "${xen_bin} create ${var_xdom_cfg} 2>&1" ); then

			logCmd 1 "${str_xen_crf} '${var_xdom}': ${var_err}"
			return 2
		fi

		return 0
	}


	# shutdown xen domU <name> via xen bus
	# uses: opts[dry_run, xen_timeout]
	# param: <xen domain name>
	#        <ssh connection string>
	# rc: 0 success
	#     1 already stopped
	#     2 failed to shutdown
	xenShutdown() {

		local var_xdom="${1}"
		local var_ussh="${2}"
		local var_err=""
		local -i count=1
		local var_force=""

		# test for binaries locally or remote
		checkBinSave "xen" "${xen_bin_lst[*]}" "${var_ussh}" || return 1

		if ! ${var_ussh:-eval} "${xen_bin} list 2>/dev/null | \
		   ${grep_bin} -q -E '^${var_xdom}[[:space:]]+'"; then

			logCmd 3 "${str_xen_nr} '${var_xdom}'"
			return 1
		fi

		logCmd 3 "${str_xen_sd} '${var_xdom}'"

		if [ -n "${opts['dry_run']}" ]; then
			logCmd 4 "${var_ussh:+${var_ussh} }${xen_bin} shutdown ${var_xdom}"
			return 0
		fi

		# use acpi shutdown in any case
		if [ ${opts['xen_force_start']} -ne 0 ]; then
			var_force="-F"
		fi

		if ! var_err=$( ${var_ussh:-eval} \
		   "${xen_bin} shutdown ${var_force} ${var_xdom} 2>&1 >/dev/null" ); then

			logCmd 4 "${str_xen_pvsdf} '${var_xdom}': ${var_err}"

			var_force="-F"
			if ! var_err=$( ${var_ussh:-eval} \
			    "${xen_bin} shutdown ${var_force} ${var_xdom} 2>&1 >/dev/null" ); then

				logCmd 1 "${str_xen_acpisdf} '${var_xdom}': ${var_err}"
				return 2
			fi
		fi

		while ${var_ussh:-eval} "${xen_bin} list 2>/dev/null" | \
		      ${grep_bin} -q -E "^${var_xdom}[[:space:]]+" && \
		      [ ${count} -lt ${opts['xen_timeout']} ]; do

			logCmd 4 "${str_xen_sdw} (${count}) '${var_xdom}'"

			# retry shutdown every 20 seconds
			if (( ${count} % 20 == 0 )); then

				logCmd 3 "${str_xen_sdr} '${var_xdom}'"
				${var_ussh:-eval} "${xen_bin} shutdown \
					${var_force} ${var_xdom} >/dev/null 2>&1"
			fi

			count+=1
			sleep 1
		done

		if [ ${count} -ge ${opts['xen_timeout']} ]; then

			logCmd 1 "${str_xen_sd} '${var_xdom}' ${str_xen_tor} ${opts['xen_timeout']}"
			return 2
		fi

		# we wait a moment after domU finished shutdown before
		# we continue to give the devices a moment to settle
		sleep 5
		return 0
	}


	# lookup if a xen domU has to be started, remove info about domU
	# uses: opts[xen_dom, lib_path], src_path[ussh, uhost]
	xenStart() {

		local var_snap=${1:true}
		local var_xdom="${opts['xen_dom']}"
		local var_ussh="${src_path['ussh']}"
		local var_uhost="${src_path['uhost']}"
		local var_err=""
		local var_lfile="${opts['lib_path']}/xendom.lst"
		local -i var_rc=0

		# nothing to do
		if [ -z "${var_xdom}" ]; then
			return 0
		fi

		# if var_snap is set, we only proceed if src is a snapshot
		if ${var_snap} && \
		   ! isSnapshot "${src_path['mdev']}" "${var_ussh}"; then

			logCmd 4 "${str_xen_nosnap} ${var_xdom} (${src_path['mdev']})"
			return 0
		fi

		if ${grep_bin} -q -E "^${var_uhost}${var_xdom}$" \
		   "${var_lfile}" 2>/dev/null || \
		   [ ${opts['xen_force_start']} -ne 0 ]; then

			xenCreate "${var_xdom#*:}" "${var_ussh}"
			var_rc=${?}
			# domU start failed, or running?
			[ ${var_rc} -gt 1 ] && return ${var_rc}

			logCmd 4 "${str_xen_rxdl} '${var_xdom}'"

			if ! var_err=$( ${sed_bin} --in-place \
			                "/^${var_uhost}${var_xdom}$/d" \
			                "${var_lfile}" 2>&1 >/dev/null ); then

				logCmd 1 "${str_xen_rxdlf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	# lookup if a xen domU has to be stopped, save info about domU
	# uses: opts[xen_dom, lib_path], src_path[ussh, uhost]
	xenStop() {

		local var_xdom="${opts['xen_dom']}"
		local var_ussh="${src_path['ussh']}"
		local var_uhost="${src_path['uhost']}"
		local var_err=""
		local var_lfile="${opts['lib_path']}/xendom.lst"
		local -i var_rc=0

		# nothing to do
		if [ -z "${var_xdom}" ]; then
			return 0
		fi

		xenShutdown "${var_xdom}" "${var_ussh}"
		var_rc=${?}
		# domU stop failed, or not running?
		[ ${var_rc} -ne 0 ] && return ${var_rc}

		if ${grep_bin} -q -E "^${var_uhost}${var_xdom}$" \
		   "${var_lfile}" 2>/dev/null; then

			logCmd 2 "${str_xen_xidl} '${var_xdom}'"
		else
			logCmd 4 "${str_xen_axdl} '${var_xdom}'"

			if ! var_err=$( echo "${var_uhost}${var_xdom}" 2>&1 >> \
			                "${var_lfile}" ); then

				logCmd 1 "${str_xen_axdlf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	puOperate() {

		local -A var_op_lst=(
			['state']='-g'
			['on']='-o'
			['off']='-f'
			)
		local var_dev="${1}"
		local var_puo="${2}"
		local var_op="${var_op_lst[${3}]}"
		local var_ussh="${4}"
		local -i rc=0

		local -i var_state=$( ${var_ussh:-eval} "sispmctl -n -q -D \
			'${var_dev}' ${var_op} '${var_puo}' 2>/dev/null" )

		rc=${?}
		case ${rc} in

			0)
				return ${var_state:-0}
			;;

			248)
				logCmd 1 "${str_pu_nde} '${var_dev}'"
				return 2
			;;

			250)
				logCmd 1 "${str_pu_noe} '${var_puo}'"
				return 2
			;;

			*)
				logCmd 1 "${str_pu_uerc}"
				return 3
			;;
		esac
	}


	puContol() {

		local var_dev="${1}"
		local var_puos=( ${2//[,;]/ } )
		local var_cmd="${3}"
		local var_ussh="${4}"
		local var_puo=""
		local -i rc=0

		[ -z "${var_dev}" ] || [ -z "${var_puos[*]}" ] && return 0
		checkBinSave "po" "${po_bin_lst[*]}" "${var_ussh}" || return 3

		for var_puo in "${var_puos[@]}"; do

			puOperate "${var_dev}" "${var_puo}" "state" "${var_ussh}"
			rc=${?}
			if [ ${rc} -gt 1 ]; then
				return ${rc}
			fi

			case "${var_cmd}" in

				on)
					if [  ${rc} -eq 1 ]; then
						logCmd 2 "${str_pu_oao} ${var_puo}"
						return 1
					fi
				;;

				off)
					if [  ${rc} -eq 0 ]; then
						logCmd 2 "${str_pu_oaf} ${var_puo}"
						return 1
					fi
				;;

				*)
					logCmd 1 "${str_pu_uc} '${var_cmd}'"
				;;
			esac

			puOperate "${var_dev}" "${var_puo}" "${var_cmd}" "${var_ussh}"
			rc=${?}
			if [ ${rc} -gt 0 ]; then
				logCmd 1 "${str_pu_cf} 'device: ${var_dev}, outlet: ${var_puo}, ${var_cmd}'"
				return ${rc}
			fi
			logCmd 3 "${str_pu_cs} 'device: ${var_dev}, outlet: ${var_puo}, ${var_cmd}'"
		done
		return 0
	}


	getUsbDev() {

		local var_blkdev="${1}"
		local var_ussh="${2}"
		local find_expr="${find_bin} /dev -maxdepth 4 -mount -path"
		local IFS_save="${IFS}"
		local -a var_slst=()

		logCmd 4 "${str_q_path_sr} '${var_blkdev}'"

		# only use absolute /dev path or a relative path we search in /dev
		if [ "${var_blkdev:0:1}" == "/" ] && \
		   [ "${var_blkdev:0:5}" != "/dev/" ]; then

			logCmd 1 "${str_gusb_cce}"
			return 2
		fi

		# prepend search path if it isn't an absolute path in dev
		if [ "${var_blkdev:0:1}" != "/" ]; then
			var_blkdev="*/${var_blkdev}"
		fi

		# search for device in /dev/
		IFS=$'\x0a'
		var_slst=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} "${find_expr} \
			   '${var_blkdev}' 2>/dev/null" | ${grep_bin} -v -E '^/dev/block' ) )
		IFS="${IFS_save}"

		# should be exact one device.
		# if we got more, the device config is crap
		if [ ${#var_slst[@]} -gt 1 ]; then
			logCmd 1 "${str_gusb_cce}"
			return 2

		# we found no device
		elif [ ${#var_slst[@]} -eq 0 ]; then
			logCmd 4 "${str_gusb_ufd} '${var_blkdev}'"
			return 1
		fi

		# get base device
		var_blkdev=$( ${var_ussh:-eval} "readlink -f '${var_slst[0]}'" )

		# we found a device
		logCmd 4 "${str_gusb_fbd} ${var_blkdev}"
		echo "${var_blkdev}"
		return 0
	}


	# options: device to save to activate list
	# uses: opts[lib_path]
	actSave() {

		local var_dev="${1}"
		local var_err=""

		if ${grep_bin} -q -E "^${var_dev}$" \
		   "${opts['lib_path']}/activate.lst" 2>/dev/null; then

			logCmd 2 "${str_lvm_ch_dial} '${var_dev}'"
		else
			logCmd 4 "${str_lvm_ch_adal} '${var_dev}'"
			if ! var_err=$( echo "${var_dev}" 2>&1 >> \
			                "${opts['lib_path']}/activate.lst" ); then

				logCmd 1 "${str_lvm_ch_adalf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	# options: device to deactivate and remove from activate list, ussh
	# uses: opts[lib_path]
	actRemove() {

		local var_dev="${1}"
		local var_local="${2}"
		local var_ussh="${3}"
		local var_err=""

		if [ -n "${var_dev}" ] && \
		   ${grep_bin} -q "${var_dev}" \
			"${opts['lib_path']}/activate.lst" 2>/dev/null; then

#			lvChange "${var_dev/#*:}" 0 ${var_local} 0 0 "${var_ussh}"
			lvChange "${var_dev#*:}" 0 ${var_local} 0 0 "${var_ussh}"
			[ ${?} -ne 0 ] && return 1

			logCmd 4 "${str_lvm_ch_rdal} '${var_dev}'"
			if ! var_err=$( ${sed_bin} --in-place "/^${var_dev//\//\\/}$/d" \
			                "${opts['lib_path']}/activate.lst" 2>&1 >/dev/null ); then

				logCmd 1 "${str_lvm_ch_rdalf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	# options: device to save to mount list
	# uses: opts[lib_path]
	mntSave() {

		local var_dev="${1}"
		local var_err=""

		if ${grep_bin} -q -E "^${var_dev}$" \
		   "${opts['lib_path']}/mount.lst" 2>/dev/null; then

			logCmd 2 "${str_mnt_diml} '${var_dev}'"
		else
			logCmd 4 "${str_mnt_adml} '${var_dev}'"

			if ! var_err=$( echo "${var_dev}" 2>&1 >> \
			                "${opts['lib_path']}/mount.lst" ); then

				logCmd 1 "${str_mnt_admlf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	# options: device to unmount and remove from mount list, ussh
	# uses: opts[lib_path]
	mntRemove() {

		local var_dev="${1}"
		local var_ussh="${2}"
		local var_err=""
		local var_dev2=""

		if [ -n "${var_dev}" ] && \
		   var_dev2=$( ${grep_bin} -E "^${var_dev}$" \
		   "${opts['lib_path']}/mount.lst" 2>/dev/null ); then

#			umountDev "${var_dev/#*:}" "${var_ussh}"
			umountDev "${var_dev2#*:}" "${var_ussh}"
			[ ${?} -ne 0 ] && return 1

			logCmd 4 "${str_mnt_rdml} '${var_dev2}'"

			if ! var_err=$( ${sed_bin} --in-place "/^${var_dev//\//\\/}$/d" \
			                "${opts['lib_path']}/mount.lst" 2>&1 >/dev/null ); then

				logCmd 1 "${str_mnt_rdmlf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	ptableSave() {

		eval "local var_path=\${${1}['mapper']}"
		eval "local var_ussh=\${${1}['ussh']}"
		eval "local var_uhost=\${${1}['uhost']}"
		local -a var_plst=()
		local var_name=""
		local var_r=""
		local var_err=""
		local -i count=0
		local -i var_timeout=10
		local var_lfile="${opts['lib_path']}/ptable.lst"
		local var_loop=true

		# test for binaries locally or remote
		checkBinSave "dskutl" "${dskutl_bin_lst[*]}" "${var_ussh}" || return 1

		# activate partition table on device
		while ! var_err=$( ${var_ussh:-eval} \
		      "${kpartx_bin} -a ${var_path} 2>&1" ) && \
		      [ ${count} -lt ${var_timeout} ]; do

			logCmd 4 "${str_pt_ar} (${count}) ${var_uhost}${var_path}'"
			count+=1
			sleep 1
		done

		if [ ${count} -ge ${var_timeout} ]; then

			logCmd 1 "${str_pt_actf} '${var_err}'"
			return 1
		fi

		logCmd 4 "${str_pt_act} '${var_uhost}${var_path}'"

		# save activated partition table device to list
		if ${grep_bin} -q -E "^${var_uhost}${var_path}$" \
		   "${var_lfile}" 2>/dev/null; then

			logCmd 2 "${str_pt_dipl} '${var_uhost}${var_path}'"
		else
			logCmd 4 "${str_pt_adpl} '${var_uhost}${var_path}'"

			if ! var_err=$( echo "${var_uhost}${var_path}" 2>&1 >> \
					"${var_lfile}" ); then

				logCmd 1 "${str_pt_adplf} '${var_err}'"
			fi
		fi

		# get partitions list from device
		while read var_name var_r; do
			var_plst+=( "/dev/mapper/${var_name}" )
		done < <( ${var_ussh:-eval} "${kpartx_bin} -l ${var_path}" )
		logCmd 4 "${str_pt_lst} ${var_plst[*]}"

		# wait until device mapper found all partitions
		count=0
		while ${var_loop} && \
		      [ ${count} -lt ${var_timeout} ]; do

			var_loop=false
			for pn in "${var_plst[@]}"; do
				${var_ussh:-eval} "${test_bin} -b '${pn}'" || var_loop=true
			done
			count+=1
			sleep 1
		done

		if [ ${count} -ge ${var_timeout} ]; then

			logCmd 1 "${str_pt_actf} ${str_pt_ptna}"
			return 1
		fi

		echo "${var_plst[@]}"
		return 0
	}


	ptableDeactivate() {

		eval "local var_path=\${${1}['mapper']}"
		eval "local var_ussh=\${${1}['ussh']}"
		eval "local var_uhost=\${${1}['uhost']}"
		local -i count=0
		local -i var_timeout=10
		local var_err=""

		# test for binaries locally or remote
		checkBinSave "dskutl" "${dskutl_bin_lst[*]}" "${var_ussh}" || return 1

		# deactivate partition table on device
		while ! var_err=$( ${var_ussh:-eval} \
		      "${kpartx_bin} -d ${var_path} 2>&1" ) && \
		      [ ${count} -lt ${var_timeout} ]; do

			logCmd 4 "${str_pt_dr} (${count}) ${var_uhost}${var_path}"
			count+=1
			sleep 1
		done

		if [ ${count} -ge ${var_timeout} ]; then

			logCmd 1 "${str_pt_deactf} '${var_err}'"
			return 1
		fi

		logCmd 4 "${str_pt_deact} '${var_path}'"
		return 0
	}


	ptableRemove() {

		eval "local var_path=\${${1}['mapper']}"
		eval "local var_ussh=\${${1}['ussh']}"
		eval "local var_uhost=\${${1}['uhost']}"
		local var_err=""
		local var_lfile="${opts['lib_path']}/ptable.lst"
		local sed_expr="/^${var_uhost}${var_path//\//\\/}\$/d"

		if ! containsElement "${opts['method']}" \
		   "partimage" "partclone" "ntfsclone"; then

			return 0
		fi

		if ${grep_bin} -q -E "^${var_uhost}${var_path}$" \
			"${var_lfile}" 2>/dev/null; then

			ptableDeactivate "${1}" || return ${1}

			# remove deactivated partition table device from list
			logCmd 4 "${str_pt_rdpl} '${var_uhost}${var_path}'"
			if ! var_err=$( ${sed_bin} --in-place -e "${sed_expr}" \
			                "${var_lfile}" 2>&1 >/dev/null ); then

				logCmd 1 "${str_pt_rdplf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	# options: block device sting to remove from usb suspend list
	# or       usb device sting to remove from usb suspend list
	# uses: opts[lib_path]
	usbSusRemove() {

		local var_dev="${1}"
		local var_err=""

		if [ -n "${var_dev}" ] && \
		   ${grep_bin} -q -E "${var_dev}" \
		         "${opts['lib_path']}/usb_suspend.lst" 2>/dev/null; then

			logCmd 4 "${str_usbs_rdsl} '${var_dev}'"

			if ! var_err=$( ${sed_bin} --in-place "/${var_dev//\//\\/}/d" \
					"${opts['lib_path']}/usb_suspend.lst" 2>&1 >/dev/null ); then

				logCmd 1 "${str_usbs_rdslf} '${var_err}'"
				return 1
			fi
		fi

		return 0
	}


	# options: device sting to add to usb suspend list
	# uses: opts[lib_path]
	usbSusSave() {

		local var_dev="${1}"
		local var_usbdev="${2}"
		local var_err=""

		if [ -n "${var_dev}" ] && \
		   ${grep_bin} -q -E "^${var_dev}" \
		         "${opts['lib_path']}/usb_suspend.lst" 2>/dev/null; then

			logCmd 2 "${str_usbs_disl} '${var_dev}'"
			if ! usbSusRemove "^${var_dev}.*$"; then
				return 1
			fi
		fi

		logCmd 4 "${str_usbs_adsl} '${var_dev}'"

		if ! var_err=$( echo "${var_dev} ${var_usbdev}" 2>&1 >> \
				"${opts['lib_path']}/usb_suspend.lst" ); then

			logCmd 1 "${str_usbs_adslf} '${var_err}'"
			return 5
		fi
	}


	# options: usb devices to resume / power up
	#          power outlet device,
	#          power outlet socket,
	#          remote user@host
	# uses: opts[lib_path,usb_dis_tries,usb_dis_sleep]
	usbResume() {

		local -a var_blkdev_grp=( ${1//[;]/ } )
		local -a var_blkdev_lst=()
		local var_lst_str=""
		local var_podev="${2}"
		local var_posoc="${3}"
		local var_uhost="${4:+${4}:}"
		local var_ussh=""
		local var_blkdev=""
		local var_usbdev=""
		local var_usb_flag=""
		local var_usb_flag_key=""
		local -a var_usb_lst=()
		local var_bdev=""
		local var_rdev=""
		local -i var_fl1=0
		local -i var_fl2=0
		local -i rc=0
		local -i var_dev_resumed=0
		local -i var_dev_count=0
		local var_blst=""
		local -i var_lock_grp=0
		local -i count=0

		if [ -z "${var_blkdev_grp[*]}" ]; then

			logCmd 3 "${str_usb_sld}"
			return 0
		fi

		# try a ssh connect, if full path is remote
		var_ussh="$( testSSH "${var_uhost}" )"
		case "${?}" in

			0)
				var_blst="${bl_bin_lst[*]}"
			;;

			1)
				var_blst="${br_bin_lst[*]}"
			;;

			*)
				return 1
			;;
		esac

		# test for binaries locally or remote
		checkBinSave "rsync" "${var_blst}" "${var_ussh}" || return 1
		checkBinSave "usb" "${usb_bin_lst[*]}" "${var_ussh}" || return 1

		logCmd 3 "${str_usb_surpu}"

		# loop through usb device groups
		for var_lst_str in "${var_blkdev_grp[@]}"; do

			var_blkdev_lst=( ${var_lst_str//[,]/ } )
			logCmd 3 "${str_usb_tddg} '${var_blkdev_lst[*]}'"
			var_dev_count=0
			var_dev_resumed=0

			# loop through usb devices within a group
			for var_blkdev in "${var_blkdev_lst[@]}"; do

				logCmd 3 "${str_usb_tdd} '${var_blkdev}'"
				var_rdev="${var_uhost}${var_blkdev}"
				var_dev_count+=1

				# start device discovery outer loop
				for ((var_fl1=0; var_fl1<2; var_fl1++)); do

					logCmd 5 "${str_usb_oudl} '${var_fl1}'"

					# start device discovery inner loop
					for ((var_fl2=0; var_fl2<${opts['usb_dis_tries']}; var_fl2++)); do

						logCmd 5 "${str_usb_dda} '$(( ${var_fl2} + 1))'"
						var_bdev="$( getUsbDev "${var_blkdev}" "${var_ussh}" )"
						rc=${?}

						if [ ${rc} -eq 0 ]; then
							logCmd 3 "${str_usb_dud} '${var_rdev}'"
							var_dev_resumed+=1
							# we found one device, so we lock on that group
							var_lock_grp=1
							# continue var_blkdev_lst loop
							continue 3

						elif [ ${rc} -gt 1 ]; then
							return 1
						fi

						# usb device isn't up at the first outer loop run
						# we have to try a wake up call to the usb device
						[ ${var_fl1} -eq 0 ] && break

						sleep ${opts['usb_dis_sleep']}
					done

					if [ ${rc} -eq 1 ] && [ ${var_fl1} -gt 0 ]; then

						# if we locked on that group, so we don't allow any misses
						if [ ${var_lock_grp} -eq 1 ]; then

							logCmd 1 "${str_usb_udd} '${var_rdev}'"
							return 1
						else
							# try next group
							continue 3
						fi

					fi

					# try to resume usb device

					if var_usb_lst=( $( ${grep_bin} -E "^${var_rdev}" \
					   "${opts['lib_path']}/usb_suspend.lst" 2>/dev/null ) ); then

						var_usbdev="${var_usb_lst[1]}"
						logCmd 3 "${str_usb_trd} '${var_uhost}${var_usbdev}'"

						# check usb device controls
						for var_usb_flag_key in "${!usbctl_flags[@]}"; do

							#skip peer as it is not mandatory
							[ ${var_usb_flag_key} == "pppm" ] && continue

							eval "var_usb_flag=${usbctl_flags[${var_usb_flag_key}]}"

							if ${var_ussh:-eval} "${test_bin} ! -e '${var_usb_flag}'"; then

								logCmd 1 "${str_usb_mdc} '${var_usb_flag}'"
								return 1
							fi
						done

						# reset usb ports to their normal operation state
						if [ ${opts['usb_pm']} -eq 1 ]; then

							# allow port peer (if exists) to stay on
							${var_ussh:-eval} "${test_bin} ! -e '${usbctl_flags['pppm']}'" && \
								${var_ussh:-eval} "echo -n '1' >${usbctl_flags['pppm']}"

							# allow port to stay on
							${var_ussh:-eval} "echo -n '1' >${usbctl_flags['ppm']}"

							# set port power control to auto
							${var_ussh:-eval} "echo -n 'auto' >${usbctl_flags['ppc']}"

							# set device power control to on
							${var_ussh:-eval} "echo -n 'on' >${usbctl_flags['dpc']}"

							# set device cfg persitent for suspend
							${var_ussh:-eval} "echo -n '1' >${usbctl_flags['dpp']}"

							# set device suspend delay to 2000
							${var_ussh:-eval} "echo -n '2000' >${usbctl_flags['dpd']}"
						fi

						# bind usb device to it's driver
						# i.e. echo -n "2-4:1.0" > /sys/bus/usb/drivers/usb-storage/bind
						if ! ${var_ussh:-eval} "echo -n '${var_usbdev##*/}' \
						     >${usbctl_flags['drvb']} 2>/dev/null"; then

							logCmd 1 "${str_usb_fr} '${var_usbdev}'"
							return 1
						fi

						# if usb pm suspend is used, we wait until
						# the usb port/device becomes active
						count=0
						while [ ${opts['usb_pm']} -eq 1 ] && \
						      [ ${count} -lt ${opts['usb_pm_timeout']} ]; do

							logCmd 4 "${str_usb_wdr} (${count}) '${var_usbdev}'"

							if [ "$( ${var_ussh:-eval} "${cat_bin} ${usbctl_flags['dps']}" )" == "active" ]; then
							#if [ "$( ${var_ussh:-eval} "${cat_bin} ${usbctl_flags['pps']}" )" == "active" ]; then

								logCmd 3 "${str_usb_dss} '${var_usbdev}'"
								break
							fi
							count+=1
							sleep 1
						done

						if [ ${count} -ge ${opts['usb_pm_timeout']} ]; then

							logCmd 1 "${str_usb_fr} '${var_usbdev}'"
							return 1
						fi

						#usbSusRemove "^${var_rdev}.*$"
						usbSusRemove "^.*${var_usbdev}$"

						continue
					else
						logCmd 4 "${str_usb_dnfsl} '${var_rdev}'"
					fi

					if [ -n "${var_podev}" ] && [ -n "${var_posoc}" ]; then

						logCmd 3 "${str_usb_tpu} '${var_uhost:+host: ${var_uhost/%:}, }device: ${var_podev}, outlet: ${var_posoc}'"

						puContol "${var_podev}" "${var_posoc}" "on" "${var_ussh}"
						rc=${?}

						if [  ${rc} -eq 1 ]; then

							logCmd 1 "${str_usb_poaon}"
							return 1

						elif [ ${rc} -gt 1 ]; then

							logCmd 1 "${str_usb_pomc}"
							return 1
						else
							sleep 5
						fi
					else
						logCmd 1 "${str_usb_pona}"

						if [ ${var_lock_grp} -eq 1 ]; then

							logCmd 1 "${str_usb_udd} '${var_rdev}'"
							return 1
						else
							continue 2
						fi
					fi
				done
			done
		done

		if [ ${var_dev_resumed} -eq 0 ] || \
		   [ ${var_dev_resumed} -ne ${var_dev_count} ]; then

			logCmd 1 "${str_usb_udd}"
			return 1
		fi

		logCmd 3 "${str_usb_adsonr}"
		return 0
	}


	# options: usb devices to suspend / power down
	#          power outlet device, power outlet socket, remote user@host
	# uses: opts[lib_path,usb_pm_timeout]
	usbSuspend() {

		local -a var_blkdev_lst=( ${1//[,;]/ } )
		local -A var_usbdev_lst=()
		local -a var_lvm_lst=()
		local var_podev="${2}"
		local var_posoc="${3}"
		local var_uhost="${4:+${4}:}"
		local var_ussh=""
		local var_blkdev=""
		local var_usbdev=""
		local var_usb_path=""
		local var_usb_flag=""
		local var_usb_flag_key=""
		local var_bdev=""
		local -a var_part_lst=()
		local var_part=""
		local var_lvm=""
		local var_vg=""
		local var_lv=""
		local var_state=""
		local -i rc=0
		local IFS_save="${IFS}"
		local find_expr="${find_bin} /dev -maxdepth 4 -mount -path"
		local lvm_opt="--noheadings --nosuffix --unbuffered --separator , "
		local lvm_opt+="--options vg_name,lv_name,lv_active"
		# pv_exported
		local -i var_suspend_fail=0
		local re_expr="s;^[^']+'(/devices/[.:/[:alnum:]-]+)/[^/]+/host[[:digit:]]+/[.:/'[:alnum:]-]+$;\1;g"
		local re_expr2='^([^,]*),([^,]*),([^,]*)$'
		local -i count=0
		local var_err=""

		# return if no usb devices have to be processed
		if [ -z "${var_blkdev_lst[*]}" ]; then

			logCmd 3 "${str_usb_sld}"
			return 0
		fi

		# try a ssh connect, if full path is remote
		var_ussh="$( testSSH "${var_uhost}" )"
		if [ ${?} -gt 1 ]; then
			return 1
		fi

		logCmd 3 "${str_usb_suspd}${var_uhost:+ (${var_uhost})}"

		# order block device by common usb devices
		# some usb devices provide several discs at once
		for var_blkdev in "${var_blkdev_lst[@]}"; do

			# get base blockdevice in /dev
			var_bdev="$( getUsbDev "${var_blkdev}" "${var_ussh}" )"
			rc=${?}
			if [ ${rc} -eq 1 ]; then
				continue

			elif [ ${rc} -gt 1 ]; then
				return 1
			fi

			# get usb device path
			var_usb_path="$( ${var_ussh:-eval} "udevadm info \
			   --query=path --name=${var_bdev} --attribute-walk" | \
			   ${grep_bin} "looking at parent device" | \
			   ${head_bin} -1 | ${sed_bin} -r -e "${re_expr}" )"

			# failed to get usb device path
			if [ -z "${var_usb_path}" ]; then
				logCmd 1 "${str_usb_fgudp} '${var_blkdev}'"
				var_suspend_fail=1
				continue
			fi

			logCmd 3 "${str_usb_fdp} '${var_blkdev}:${var_usb_path}'"

			# store block device in var_usbdev_lst
			# keyed as var_usb_path
			[ -n "${var_usbdev_lst[${var_usb_path}]}" ] && \
				var_usbdev_lst[${var_usb_path}]+=","
			var_usbdev_lst[${var_usb_path}]+="${var_blkdev}"
		done

		# we don't continue if a usb device path is missing
		if [ ${var_suspend_fail} -eq 1 ]; then

			logCmd 1 "${str_usb_rcus}"
			return 1
		fi

		# flush all buffers to be sure
		${var_ussh:-eval} "sync"

		# process the usb device path list
		# with their attached block devices
		for var_usbdev in "${!var_usbdev_lst[@]}"; do

			var_blkdev_lst=( ${var_usbdev_lst[${var_usbdev}]//[,]/ } )
			logCmd 4 "${str_usb_camp}"

			for var_blkdev in "${var_blkdev_lst[@]}"; do

				# get base blockdevice in /dev
				var_bdev="$( getUsbDev "${var_blkdev}" "${var_ussh}" )"
				if [ ${?} -ne 0 ]; then
					var_suspend_fail=1
					continue
				fi

				# check usb device for active pv's, or mounted partitions

				# get devices partitions may existing on the device
				IFS=$'\x0a'
				var_part_lst=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} "${find_expr} \
					       '${var_bdev}' 2>/dev/null" ) )
				IFS="${IFS_save}"

				# fail if we found no device
				if [ ${#var_part_lst[@]} -eq 0 ]; then
					logCmd 1 "${str_usb_dse} '${var_bdev}'"
					var_suspend_fail=1
					continue
				fi

				# test device (and each partition)
				for var_part in "${var_part_lst[@]}"; do

					# is something mounted?
					if queryMount "s" "${var_part}" "${var_ussh}"; then

						logCmd 1 "${str_usb_fmp} '${var_uhost}${var_part}'"
						var_suspend_fail=1
						continue
					fi

					# treat device/partition as pv and get logical volumes
					var_lvm_lst=( $( ${var_ussh:-eval} "${lvm_bin} pvs \
						${lvm_opt} ${pvs_opt} ${var_part} 2>/dev/null" ) )

					# is some lv active?
					for var_lvm in "${var_lvm_lst[@]}"; do

						if ! [[ "${var_lvm}" =~ ${re_expr2} ]]; then

							logCmd 1 "${str_usb_uplvm} '${var_lvm}'"
							var_suspend_fail=1
						fi

						var_vg="${BASH_REMATCH[1]}"
						var_lv="${BASH_REMATCH[2]}"
						var_state="${BASH_REMATCH[3]}"

						[ "${var_state}" == "active" ] || continue

						logCmd 4 "${str_usb_tdav} '${var_uhost}${var_vg}/${var_lv}'"

						lvChange "${var_vg}/${var_lv}" 0 0 0 0 "${var_ussh}"
						if [ ${?} -ne 0 ]; then
							var_suspend_fail=1
							continue
						fi
					done
				done

				# delete device entry
				#if ! [ -e "/sys/block/${var_bdev##*/}/device/delete" ]; then
				#
				#	logCmd 2 "${str_usb_mdc} '${${var_bdev}}'"
				#	continue
				#fi
				#${var_ussh:-eval} "echo -n '1' > '/sys/block/${var_bdev##*/}/device/delete'"
				# remove residual dev files
				#rm $(${find_bin} -L /dev -maxdepth 4 -mount -samefile ${var_bdev})
			done

			if [ ${var_suspend_fail} -eq 1 ]; then

				# we don't continue if something is still active
				logCmd 1 "${str_usb_rcus}"
				return 1
			fi

			# try to suspend usb device
			logCmd 3 "${str_usb_tsd} '${var_usbdev} (${var_blkdev_lst[*]})'"

			# check usb device controls
			for var_usb_flag_key in "${!usbctl_flags[@]}"; do

				#skip peer as it is not mandatory
				[ ${var_usb_flag_key} == "pppm" ] && continue

				eval "var_usb_flag=${usbctl_flags[${var_usb_flag_key}]}"

				if ${var_ussh:-eval} "${test_bin} ! -e '${var_usb_flag}'"; then

					logCmd 1 "${str_usb_mdc} '${var_usb_flag}'"
					var_suspend_fail=1
					continue 2
				fi
			done

			# prepare usb pm for suspend, if configured
			if [ ${opts['usb_pm']} -eq 1 ]; then

				# allow port peer (if exists) to power down
				${var_ussh:-eval} "${test_bin} ! -e '${usbctl_flags['pppm']}'" && \
					${var_ussh:-eval} "echo -n '0' >${usbctl_flags['pppm']}"

				# allow port to power down
				${var_ussh:-eval} "echo -n '0' >${usbctl_flags['ppm']}"

				# set port power control to auto
				${var_ussh:-eval} "echo -n 'auto' >${usbctl_flags['ppc']}"

				# set device power control to auto
				${var_ussh:-eval} "echo -n 'auto' >${usbctl_flags['dpc']}"

				# set device cfg persitent for suspend
				${var_ussh:-eval} "echo -n '1' >${usbctl_flags['dpp']}"

				# set device suspend delay to 0
				${var_ussh:-eval} "echo -n '0' >${usbctl_flags['dpd']}"
			fi

			# unbind usb device from it's driver
			# i.e. echo -n "2-4:1.0" > /sys/bus/usb/drivers/usb-storage/unbind
			if ! ${var_ussh:-eval} "echo -n '${var_usbdev##*/}' \
			     >${usbctl_flags['drvub']} 2>/dev/null"; then

				logCmd 1 "${str_usb_fs} '${var_usbdev}'"
				var_suspend_fail=1
				continue
			fi

			# if usb pm suspend is used, we wait until
			# the usb port/device becomes suspended
			count=0
			while [ ${opts['usb_pm']} -eq 1 ] && \
			      [ ${count} -lt ${opts['usb_pm_timeout']} ]; do

				logCmd 4 "${str_usb_wds} (${count}) '${var_usbdev}'"

				# if [ "$( ${var_ussh:-eval} "${cat_bin} ${usbctl_flags['dps']}" )" == "suspended" ] &&
				if [ "$( ${var_ussh:-eval} "${cat_bin} ${usbctl_flags['pps']}" )" == "suspended" ]; then

					logCmd 3 "${str_usb_dss} '${var_usbdev}'"
					break
				fi
				count+=1
				sleep 1
			done

			if [ ${count} -ge ${opts['usb_pm_timeout']} ]; then

				logCmd 1 "${str_usb_fs} '${var_usbdev}'"
				var_suspend_fail=1
			fi

			# save blockdevices with usb device path to list
			for var_blkdev in "${var_blkdev_lst[@]}"; do
				usbSusSave "${var_uhost}${var_blkdev}" "${var_usbdev}"
			done
		done

		# we don't continue if a usb device path is missing
		if [ ${var_suspend_fail} -eq 1 ]; then

			logCmd 1 "${str_usb_rpde}"
			return 1
		fi

		# controlling a power outlet unit
		if [ -n "${var_podev}" ] && [ -n "${var_posoc}" ]; then

			# if we don't really suspend usb devices
			# we have no way to 'see' if the really suspended
			# so we wait some time
			if [ ${opts['usb_pm']} -ne 1 ]; then

				logCmd 3 "${str_usb_wbpd} ${opts['usb_pm_timeout']} sec."
				sleep ${opts['usb_pm_timeout']}
			fi

			logCmd 3 "${str_usb_tpd} '${var_uhost:+host: ${var_uhost/%:}, }device: ${var_podev}, outlet: ${var_posoc}'"

			puContol "${var_podev}" "${var_posoc}" "off" "${var_ussh}"
			rc=${?}

			if [ ${rc} -eq 1 ]; then

				logCmd 1 "${str_usb_poaoff}"

			elif [ ${rc} -gt 1 ]; then

				logCmd 1 "${str_usb_pomc}"
				return 1
			fi

			# remove usb devices from suspend list, since is is off now
			# and reset usb ports to their normal operation state
			for var_usbdev in "${!var_usbdev_lst[@]}"; do

				if [ ${opts['usb_pm']} -eq 1 ]; then

					# allow port peer (if exists) to stay on
					${var_ussh:-eval} "${test_bin} ! -e '${usbctl_flags['pppm']}'" && \
						${var_ussh:-eval} "echo -n '1' >${usbctl_flags['pppm']}"

					# allow port to stay on
					${var_ussh:-eval} "echo -n '1' >${usbctl_flags['ppm']}"

					# set port power control to auto
					${var_ussh:-eval} "echo -n 'auto' >${usbctl_flags['ppc']}"

					# set device power control to on
					${var_ussh:-eval} "echo -n 'on' >${usbctl_flags['dpc']}"

					# set device cfg persitent for suspend
					${var_ussh:-eval} "echo -n '1' >${usbctl_flags['dpp']}"

					# set device suspend delay to 2000
					${var_ussh:-eval} "echo -n '2000' >${usbctl_flags['dpd']}"
				fi

				usbSusRemove "^.*${var_usbdev}$"
			done
		else
			logCmd 4 "${str_usb_pona}"
		fi

		logCmd 3 "${str_usb_adsoff}"
		return 0
	}


	getMntRe() {

		local var_ussh="${1}"

		if checkBinSave "mount" "${mnt_bin_lst[*]}" "${var_ussh}" 0 1; then

			echo '^(.+)\ on\ (.+)\ type\ ([^\ ]+)\ \(([^\)]+)\)$'
		else
			echo '^(.+)\ on\ (.+)\ \(([^,)]+),\ ([^\)]+)\)$'
		fi
	}


	# uses: mnt_lst
	mountPt() {

		local var_path="${1/%\/}"
		local var_ussh="${2}"
		local -a var_mtab
		local -A var_mnt
		local fn=""
		local f2=""
		local re_expr="$( getMntRe "${var_ussh}" )"
		local IFS_save="${IFS}"

		if checkBinSave "mountpoint" "${mntpt_bin_lst[*]}" "${var_ussh}" 0 1; then
			${var_ussh:-eval} "${mountpoint_bin} -q '${var_path:-/}'" && return 0
		else
			# get mount list
			IFS=$'\x0a'
			var_mtab=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} "${mount_bin}" ) )
			IFS="${IFS_save}"

			for fn in "${var_mtab[@]}"; do

				# match each mount list element
				if ! [[ "${fn}" =~ ${re_expr} ]]; then
					# parsing error
					logCmd 1 "${str_mnt_pe} '${fn}'"
					return 2
				fi
				# assign var_mnt[source,target,fstype,fsopts]
				var_mnt=()
				for f2 in "${!mnt_lst[@]}"; do
					var_mnt[${mnt_lst[${f2}]}]+="${BASH_REMATCH[${f2}+1]}"
				done

				if [ "${var_mnt['target']}" == "${var_path:-"/"}" ]; then
					return 0
				fi
			done
		fi

		return 1
	}


	# options: query type (s|t), query path, ussh
	# returns: [source,target,fstype,fsopts]
	# uses: mnt_lst
	queryMountA() {

		local var_qtype="${1}"
		local var_query="${2}"
		local var_ussh="${3}"
		local -a var_mtab
		local -a var_dlst
		local -A var_mnt
		local fn=""
		local f2=""
		if ${var_ussh:-eval} "${mount_bin} --version >/dev/null 2>&1"; then 
			local re_expr1="^(.+)\ on\ (.+)\ type\ ([^\ ]+)\ \(([^\)]+)\)$"
		else
			local re_expr1="^(.+)\ on\ (.+)\ \(([^,)]+),\ ([^\)]+)\)$"
		fi
		local find_expr="${find_bin} -L /dev -maxdepth 4 -mount -samefile"
		local IFS_save="${IFS}"

		[ -z "${var_qtype}" ] || [ -z "${var_query}" ] && return 1

		IFS=$'\x0a'
		# get mount list
		var_mtab=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} "${mount_bin}" ) )
		if [ "${var_qtype}" == "s" ]; then
			# get list of all device references
			var_dlst=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} \
			              "${find_expr} '${var_query}' 2>/dev/null" | \
			              ${grep_bin} -v -E '^/dev/block' ) )
		fi
		IFS="${IFS_save}"

		for fn in "${var_mtab[@]}"; do

			# match each mount list element
			if ! [[ "${fn}" =~ ${re_expr1} ]]; then
				# parsing error
				logCmd 1 "${str_mnt_pe} '${var_fn}'"
				return 3
			fi
			# assign var_mnt[source,target,fstype,fsopts]
			for f2 in "${!mnt_lst[@]}"; do
				var_mnt[${mnt_lst[${f2}]}]+="${BASH_REMATCH[${f2}+1]}"
			done

			if [ "${var_qtype}" == "s" ]; then

				# match var_mnt[source] with each element of var_dlst
				for f2 in "${var_dlst[@]}"; do

					if [ "${var_mnt['source']}" == "${f2}" ]; then
						logCmd 4 "${str_mnt_dm} '${f2}'"
						break 2
					fi
				done
			else
				# match var_mnt[target] with query
				if [ "${var_mnt['target']}" == "${var_query/%\/}" ]; then
					logCmd 4 "${str_mnt_pm} '${f2}'"
					break
				fi
			fi

			var_mnt=()
		done

		# skip if device is not mounted
		if [ ${#var_mnt[@]} -eq 0 ]; then
			return 1
		fi

		for fn in "${!var_mnt[@]}"; do
			echo "['${fn}']='${var_mnt[${fn}]}'"
		done

		return 0
	}


	# options: query type (s|t), query path, ussh
	# returns: [source,target,fstype,fsopts]
	queryMountB() {

		local var_qtype="${1}"
		local var_query="${2}"
		local var_ussh="${3}"
		local tmp_data=""
		local re_expr='SOURCE="([^"[]+)(\[[^]]+\])?"[[:space:]]+TARGET="([^"]+)"'
		re_expr+='[[:space:]]+FSTYPE="([^"]+)"[[:space:]]+OPTIONS="([^"]+)"'
		local findmnt_opts="--noheadings --notruncate --pairs "
		findmnt_opts+="--first-only --output SOURCE,TARGET,FSTYPE,OPTIONS"

		[ -z "${var_qtype}" ] || [ -z "${var_query}" ] && return 1

		if [ "${var_qtype}" == "s" ]; then
			findmnt_opts+=" --source"
		else
			# TODO: we should rather use --mountpoint than --target here
			# test if that works more reliable
			findmnt_opts+=" --mountpoint"
		fi

		tmp_data=$( ${var_ussh:-eval} "${findmnt_bin} ${findmnt_opts} '${var_query}'" )

		# skip if device is not mounted
		if [ ${?} -ne 0 ] || [ -z "${tmp_data}" ]; then
			return 1
		fi

		# check for parsing error
		if [[ ! "${tmp_data}" =~ ${re_expr} ]]; then
			logCmd 1 "${str_mnt_pe} '${var_query}'"
			return 3
		fi

		# fail if there is an [/dir] extension to the source, as we got a bindmount
		# FIXME: there seems to be an error in findmnt 2.25.2 as used in jessie,
		#        no [/dir] extension to the source is returned to bind mount paths
		if [ -n "${BASH_REMATCH[2]}" ]; then
			logCmd 1 "${str_mnt_rf} '${var_query}'"
			return 2
		fi

		echo "['fstype']='${BASH_REMATCH[4]}'"
		echo "['fsopts']='${BASH_REMATCH[5]}'"

		if [ "${var_qtype}" == "s" ]; then
			echo "['source']='${var_query}'"
			echo "['target']='${BASH_REMATCH[3]}'"
		else
			echo "['source']='${BASH_REMATCH[1]}'"
			echo "['target']='${var_query}'"
		fi

		return 0
	}


	queryMount() {

		if checkBinSave "findmnt" "${fmnt_bin_lst[*]}" "${3}" 0 1; then
			queryMountB "${@}"
			return ${?}
		else
			queryMountA "${@}"
			return ${?}
		fi
	}


	optsRemove() {

		local var_opts="${1}"
		local var_ropts=( ${@:2} )
		local fn=""

		for fn in "${var_ropts[@]}"; do
			var_opts="${var_opts/${fn}*([^,])?(,)}"
		done
		# [ "${var_opts: -1:1}" == "," ] && var_opts=${var_opts: 0: ${#var_opts}-1}

		echo ${var_opts/%,}
	}


	mountDev() {

		local var_src="${1}"
		local var_dst="${2}"
		local var_ussh="${3}"
		local var_opts="${4:+-o ${4//[; ]/,}}"
		local var_fstype="${5:+-t ${5}}"
		local var_skip=${6:-0}
		local var_err=""
		local tmp_data=""
		local count=0
		local -i rc=0
		local -A mnt_src
		local -A mnt_dst
		local var_pinfo=""
		local var_psuff="p?[[:digit:]]*"

		[ -z "${var_src}" ] || [ -z "${var_dst}" ] && return 1

		var_src="${var_src/%\/}"
		var_dst="${var_dst/%\/}"

		if [ -z "${var_src}" ] || [ -z "${var_dst}" ]; then
			logCmd 1 "${str_mnt_path_e}"
			return 6
		fi

		tmp_data=$( queryMount "s" "${var_src}" "${var_ussh}" )
		rc=${?}
		[ ${rc} -gt 1 ] && return 4
		eval "mnt_src=( ${tmp_data} )"

		if [ ${rc} -eq 0 ]; then

			# source is mounted
			logCmd 4 "${str_mnt_im} '${var_src} -> ${mnt_src[target]}'"
			mnt_src['fsopts']=$( optsRemove "${mnt_src['fsopts']}" ${opts['rm_mnt_opts']//[,;]/ } )

			for fn in "${!mnt_src[@]}"; do
				echo "['${fn}']='${mnt_src[${fn}]}'"
			done
			return 1

		elif [ ${var_skip} -eq 1 ]; then

			# source is not mounted, we stop here since we're asked for
			logCmd 4 "${str_mnt_sum} '${var_src}'"
			return 2
		fi

		# don't try to mount anything without filesystem
		if ! [[ "${var_fstype}" =~ cifs ]]; then

			var_pinfo=$( ${var_ussh:-eval} "${file_bin} -sL '${var_src}'" )

			if [[ "${var_pinfo}" =~ :\ data$ ]]; then
				logCmd 4 "${str_mnt_raw} '${var_src}'"
				return 7
			fi

			if [[ ! "${var_pinfo}" =~ (filesystem\ data|NTFS|FAT) ]]; then
				logCmd 2 "${str_mnt_nofs} '${var_src}'"
				return 3
			fi
		fi

		# run fsck for snapshots
		if [[ "${var_src}" =~ ^.+${opts['snap_suffix']}${var_psuff}$ ]] && \
		   [ ${opts['snap_fsck']} -eq 1 ]; then

			logCmd 4 "${str_fs_chk} '${var_src}'"
			if [[ "${var_pinfo}" =~ NTFS ]]; then
				if [ -n "${opts['dry_run']}" ]; then 
					var_err=$( ${var_ussh:-eval} "${ntfsfix_bin} '${var_src}' 2>&1" )
					if [ ${?} -ne 0 ]; then
						logCmd 1 "${str_fs_chk_f} '${var_err}'"
						return 5
					fi
				fi
			else
				var_err=$( ${var_ussh:-eval} "${fsck_bin} -fa '${var_src}' 2>&1" )
				if (( ${?} & 4 )); then
					logCmd 1 "${str_fs_chk_f} '${var_err}'"
					return 5
				fi
			fi
			logCmd 5 "[out]: '${var_err}'"
		fi

		# create mount target
		pathCreate "${var_dst}" "${var_ussh}"
		[ ${?} -ne 0 ] && return 4

		# test destination
		if mountPt "${var_dst}" "${var_ussh}"; then

			# something is already mounted at the target
			logCmd 1 "${str_mnt_mo} '${var_dst}'"
			return 4
		fi

		logCmd 4 "${str_mnt_m} '${var_src} -> ${var_dst}'"

		# mount source
		for ((count = 1 ; count <= 10 ; count++)); do

			sleep 1
			if var_err=$( ${var_ussh:-eval} "${mount_bin} ${var_fstype} ${var_opts} \
			              '${var_src}' '${var_dst}' 2>&1 >/dev/null" ); then

				logCmd 4 "${str_mnt_s} '${var_src} -> ${var_dst}'"

				tmp_data=$( queryMount "s" "${var_src}" "${var_ussh}" )
				[ ${?} -gt 1 ] && return 4
				eval "mnt_dst=( ${tmp_data} )"
				mnt_dst['fsopts']=$( optsRemove "${mnt_dst['fsopts']}" ${opts['rm_mnt_opts']//[,;]/ } )

				for fn in "${!mnt_dst[@]}"; do
					echo "['${fn}']='${mnt_dst[${fn}]}'"
				done
				return 0
			else
				logCmd 4 "${str_mnt_r} '${count} : ${var_src}'"
			fi
		done

		# we where unable to mount after 10 tries
		logCmd 1 "${str_mnt_f} '${var_err}'"
		return 4
	}


	umountDev() {

		local var_path=${1}
		local var_ussh=${2}
		local var_err=""
		local tmp_data=""
		local -i rc=0
		local -A var_mnt

		[ -z "${var_path}" ] && return 1

		tmp_data=$( queryMount "s" "${var_path}" "${var_ussh}" )
		rc=${?}
		[ ${rc} -eq 1 ] && return 0
		[ ${rc} -gt 1 ] && return 2
		eval "var_mnt=( ${tmp_data} )"

		for ((count = 1 ; count <= 10 ; count++)); do

			sleep 1
			if var_err=$( ${var_ussh:-eval} "${umount_bin} '${var_path}' 2>&1 >/dev/null" ); then
				logCmd 4 "${str_umnt_s} '${var_path}'"
				pathRemove "${var_mnt['target']}" "${var_ussh}"
				return 0
			else
				logCmd 4 "${str_umnt_r} '${count} : ${var_path}'"
			fi
		done

		# we where unable to unmount after 10 tries
		logCmd 1 "${str_umnt_f} '${var_err}'"
		return 1
	}


	# uses: dst_path[udev,source,ussh], (opts[lib_path])
	#       src_path[udev,mdev,source,ussh]
	Cleanup () {

		# umount destination device
		mntRemove "${dst_path['udev']}" "${dst_path['ussh']}"

		# umount source device
		mntRemove "${src_path['udev']}" "${src_path['ussh']}"

		# umount source sub partition, if necessary
		mntRemove "${src_path['uhost']}${src_path['mapper']}" "${src_path['ussh']}"

		# deactivate source and destination sub partition table
		ptableRemove "dst_path"
		ptableRemove "src_path"

		# remove backup snapshot, if we have one
		if isSnapshot "${src_path['mdev']}" "${src_path['ussh']}"; then

			snapRemove "${src_path['mdev']}" "${src_path['ussh']}"
		fi

		# deactivate destination LV
		actRemove "${dst_path['adev']}" ${opts['lv_dst_al']} "${dst_path['ussh']}"

		# deactivate source LV
		actRemove "${src_path['adev']}" ${opts['lv_src_al']} "${src_path['ussh']}"

		# start xen domain
		xenStart false

		return 0
	}


	# TODO: is var_bs_file obsolete?
	# test ssh connection
	testSSH() {

		local var_uhost="${1/%:}"
		local var_ussh=""
		local var_bs_file="${opts['lib_base']}/bs/localhost"
		local ssh_opts="-q -x -n -o UserKnownHostsFile=/dev/null "
		ssh_opts+="-o StrictHostKeyChecking=no -o BatchMode=yes"

		if [ -n "${var_uhost}" ]; then

			checkBinSave "ssh" "${ssh_bin_lst[*]}" || return 2
			var_ussh="${ssh_bin} ${ssh_opts} ${var_uhost}"

			if ! ${var_ussh} exit >/dev/null 2>&1; then

				logCmd 1 "${str_q_path_ssh_nc} '${var_uhost}'"
				return 2
			fi

			logCmd 4 "${str_q_path_ssh_c} '${var_uhost}'"
			echo "${var_ussh}"
			return 1
		fi

		return 0
	}


	querySmbPath() {

		local var_full="${1}"
		local var_dom=""
		local var_user=""
		local var_pwd=""
		local var_smb=""
		local var_host=""
		local var_share=""
		local var_path=""
		local var_fstype=""
		local var_fsopts=""

		# split path into user, host and actual path part, if possible
		if [[ "${var_full}" =~ ^(smb|cifs)://((([^:]*):)?([^:]+):([^@]+)@)?(([^/]+)/([^/]+)/?(.*))$ ]]; then

			var_dom="${BASH_REMATCH[4]}"
			var_user="${BASH_REMATCH[5]}"
			var_pwd="${BASH_REMATCH[6]}"
			var_smb="//${BASH_REMATCH[7]}"
			var_host="${BASH_REMATCH[8]}"
			var_share="${BASH_REMATCH[9]}"
			var_path="${BASH_REMATCH[10]}"
		else
			logCmd 1 "${str_q_path_upsmb} '${var_full}'"
			return 1
		fi

		var_fsopts+="${var_dom:+domain=${var_dom},}"
		var_fsopts+="${var_user:+username=${var_user},}"
		var_fsopts+="${var_pwd:+password=${var_pwd}}"
		echo "['fsopts']='${var_fsopts}'"
		echo "['fstype']='cifs'"
		echo "['smbhost']='${var_host}'"
		echo "['smbshare']='${var_share}'"
		echo "['smbpath']='${var_path}'"
		echo "['type']='cifs'"
		echo "['dpath']='${var_smb}'"
		echo "['path']='${var_smb}'"
		echo "['lvs']='-'"

		return 0
	}


	# options:
	# provides:
	# echoes: full = full raw path 'user@host:path'
	#         path = local raw path
	#        uhost = remote raw path 'user@host:'
	#         ussh = full ssh connection string
	#          lvs = vg_name,lv_name,lv_size,vg_size,vg_free,lv_attr
	#    lvs (vgs) = vg_name,vg_size,vg_free
	#        dpath = final dir or device path
	#         type = dir|bd|vg|tp|lv|na
	# uses: 
	queryPath() {

		local var_full="${1}"
		local var_path=""
		local var_ussh=""
		local var_type=""
		local var_ext=""
		local var_dpath=""
		local var_relpath=""
		local var_user=""
		local var_host=""
		local -a var_plst1=()
		local -a var_plst2=()
		local var_p=""
		local lvm_opt="--noheadings --nosuffix --unbuffered --separator , --units b"
		local lvs_opt="--options vg_name,lv_name,lv_size,vg_size,vg_free,lv_attr ${lvm_opt}"
		local vgs_opt="--options vg_name,vg_size,vg_free ${lvm_opt}"
		local -a lvs_out=()
		local -a vgs_out=()
		local find_expr1="${find_bin} /dev -maxdepth 4 -mount -path"
		local find_expr2="${find_bin} -L /dev -maxdepth 4 -mount -samefile"
		local -i rc=0
		local IFS_save="${IFS}"
		local var_blst=""
		local -i var_lvm=1
		local var_err=""

		# check for empty full path
		if [ -z "${var_full}" ]; then
			logCmd 1 "${str_q_path_fpz}"
			return 1
		fi

		# remove concluding slash from path
		# except if we have just "/" or some "name@server:/"
		if [[ ! "${var_full}" =~ ^(.+:)?/$ ]]; then
			var_full="${var_full/%\/}"
		fi
		echo "['full']='${var_full}'"

		# catch windows smb path description
		if [[ "${var_full}" =~ ^(smb|cifs):// ]]; then
			querySmbPath "${var_full}"
			return ${?}
		fi

		# split path into user, host and actual path part, if possible
		if [[ ! "${var_full}" =~ (.+)@(.+):(.+) ]]; then
			var_path="${var_full}"
		else
			var_user="${BASH_REMATCH[1]}"
			var_host="${BASH_REMATCH[2]}"
			var_path="${BASH_REMATCH[3]}"
		fi

		# check for empty path
		if [ -z "${var_path}" ]; then
			logCmd 1 "${str_q_path_pz} '${var_full}'"
			return 1
		fi
		echo "['path']='${var_path}'"

		# try a ssh connect, if full path is remote
		var_ussh="$( testSSH "${var_user:+${var_user}@}${var_host}" )"
		case "${?}" in

			0)
				var_blst="${bl_bin_lst[*]}"
			;;

			1)
				echo "['uhost']='${var_user}@${var_host}:'"
				echo "['ussh']='${var_ussh}'"
				var_blst="${br_bin_lst[*]}"
			;;

			*)
				return 1
			;;
		esac

		# test for binaries locally or remote
		checkBinSave "rsync" "${var_blst}" "${var_ussh}" || return 1
		checkBinSave "lvm" "${lvm_bin_lst[*]}" "${var_ussh}" 0 1
		var_lvm=${?}

		# remove broken symlinks in /dev/mapper/ (residuals from device mapper ?)
		if ${var_ussh:-eval} "${test_bin} -d '/dev/mapper/'" ; then
			if ! var_err=$( ${var_ussh:-eval} \
				"${find_bin} -L /dev/mapper/ -maxdepth 4 -mount -type l -delete 2>&1" ); then

				logCmd 2 "${str_q_path_erbsl} ${var_err}"
			fi
		fi

		# figure out the type of path: normal dir., VG, LV, blockdev. ...

		# expand var_path to a list, and find reference in /dev/
		IFS=',;'
		var_plst1=( ${var_path} )
		IFS="${IFS_save}"
		for var_p in "${var_plst1[@]}"; do

			# search for device in /dev/
			if [[ ! "${var_p}" =~ ^/ ]] || [[ "${var_p/%\/}/" =~ ^/dev/ ]]; then

				logCmd 4 "${str_q_path_sr} '${var_p}'"

				if [[ ! "${var_p}" =~ ^/ ]]; then
					var_p="*/${var_p}"
				fi

				IFS=$'\x0a'

				var_plst2=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} "${find_expr1} \
				  '${var_p}' 2>/dev/null" | ${grep_bin} -v -E '^/dev/block' ) )

				# search for all device references in /dev
				# if first search was successfull
				if [ ${#var_plst2[@]} -ne 0 ]; then
					var_plst2+=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} "${find_expr2} \
					  '${var_plst2[0]}' 2>/dev/null" | ${grep_bin} -v -E '^/dev/block' ) )
				fi

				IFS="${IFS_save}"
			fi

			# replacing var_plst1 if search in /dev/ was successfull
			if [ ${#var_plst2[@]} -ne 0 ]; then
				var_plst1=( "${var_plst2[@]}" )
				logCmd 4 "${str_q_path_dp} '${var_plst1[*]}'"
				break
			fi
		done

		# work through path list
		for var_p in "${var_plst1[@]}"; do

			logCmd 4 "${str_q_path_tst} '${var_p}'"

			# check for a normal directory
			if [[ "${var_p}" =~ ^/ ]] && [[ ! "${var_p}/" =~ ^/dev/ ]]; then

				if ${var_ussh:-eval} "${test_bin} -d '${var_p}'" ; then

					# we are processing a normal directory as source
					logCmd 4 "${str_q_path_d} '${var_p}'"
					var_type="dir"
					var_dpath="${var_p}"
					break
				else
					# skip absolute, non /dev/* paths, matched as a directory
					continue
				fi
			fi

			# we may processing a blockdevice of some sort
			logCmd 4 "${str_q_path_bdtst} '${var_p}'"

			# check for blockdevice
			if [[ "${var_p}/" =~ ^/dev/ ]] && \
			   ${var_ussh:-eval} "${test_bin} -b '${var_p}'"; then

				# path is a blockdevice but not a LV
				logCmd 4 "${str_q_path_bd} '${var_p}'"
				var_type="bd"
				[ -z "${var_dpath}" ] && var_dpath="${var_p}"

				# if we found a block device and have no lvm we can break here
				[ ${var_lvm} -ne 0 ] && break
			fi

			# end here in case that there is no lvm
			[ ${var_lvm} -ne 0 ] && continue

			# retrieve lvs infos if available
			# first trigger a pvscan, so we know all VG's and LV's
			${var_ussh:-eval} "${lvm_bin} pvscan >/dev/null 2>&1"
			IFS=$'\x0a'
			lvs_out=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} \
			             "${lvm_bin} lvs ${lvs_opt} '${var_p}' 2>/dev/null" | \
			             ${sed_bin} -r -e 's,^[[:space:]]*,,g' ) )

			# try to get empty VG's
			if [ ${#lvs_out[@]} -eq 0 ] && [[ ! "${var_p}" =~ / ]]; then
				vgs_out=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} \
				             "${lvm_bin} vgs ${vgs_opt} '${var_p}' 2>/dev/null" | \
				             ${sed_bin} -r -e 's,^[[:space:]]*,,g' ) )
			fi
			IFS="${IFS_save}"

			# check for LVM
			if [ ${#lvs_out[@]} -ne 0 ] || [ ${#vgs_out[@]} -ne 0 ]; then

				if [ "${var_path/#*\/}" == "${lvs_out[0]/%,*}" ] || \
				   [ ${#vgs_out[@]} -ne 0 ]; then

					# we have a volume group
					logCmd 4 "${str_q_path_vg} '${var_p}'"
					var_type="vg"

				elif [[ "${lvs_out[0]/#*,}" =~ ^t.....t ]]; then

					# we have a thin pool
					logCmd 4 "${str_q_path_tp} '${var_p}'"
					var_type="tp"

					# in case of a thin pool, we need the full VG information instead
					IFS=$'\x0a'
					lvs_out=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} \
						     "${lvm_bin} lvs ${lvs_opt} '${lvs_out[0]/%,*}' 2>/dev/null" | \
						     ${sed_bin} -r -e 's,^[[:space:]]*,,g' ) )
					IFS="${IFS_save}"
				else
					# we have a logical volume
					logCmd 4 "${str_q_path_lv} '${var_p}'"
					var_type="lv"
				fi

				if [ ${#lvs_out[@]} -eq 0 ]; then

					# empty volume group
					logCmd 4 "${str_q_path_evg}"
					echo "['lvs']='${vgs_out[0]/%,*}'"
				else
					if [[ ! "${var_p}/" =~ ^/dev/ ]] && \
					   [ "${var_type}" == "vg" ]; then

						# inactive volume group
						logCmd 3 "${str_q_path_iavg} '${var_p}'"
					fi

					echo "['lvs']='${lvs_out[@]}'"
				fi

				var_dpath="${var_p}"
				break
			fi
		done

		if [ -z "${var_type}" ]; then
			# can't figure out what we have
			logCmd 4 "${str_q_path_nt} '${var_path}'"
			echo "['type']='na'"
			return 1
		fi

		echo "['type']='${var_type}'"
		echo "['dpath']='${var_dpath}'"

		# ensure that something is in 'lvs'
		if [[ ! "${var_type}" =~ ^(vg|tp|lv)$ ]]; then
			echo "['lvs']='-'"
		fi

		return 0
	}


	parseSize() {

		local var_size="${1}"
		local -i var_num=0
		local var_suff=""
		local -i var_size_b=0
		local re_expr='^[[:space:]]*([[:digit:]]+(.[[:digit:]]+)?)'
		re_expr+='[[:space:]]*([kKmMgGtT]?)[[:space:]]*$'

		local -A var_suff_t=(
			['1']=1
			['k']=1000
			['K']=1024
			['m']=1000000
			['M']=1048576
			['g']=1000000000
			['G']=1073741824
			['t']=1000000000000
			['T']=1099511627776
		)

		if ! [[ "${var_size}" =~ ${re_expr} ]]; then

			logCmd 1 "${str_p_pse} '${var_size}'"
			return 1
		fi

		var_num=${BASH_REMATCH[1]}
		var_suff=${BASH_REMATCH[3]:-1}
		var_size_b=$( ${bc_bin} <<< "scale=0; ${var_num} * ${var_suff_t[${var_suff}]}" )

		echo "${var_size_b}"
		return 0
	}


	checkDF() {

		local var_mp="${1}"
		local var_df_warn="${2:-0}"
		local -i var_df_warn_b=0
		local var_df_str=""
		local var_ussh="${3}"
		local re_expr='^([._/[:alnum:]-]+)[[:space:]]+([[:digit:]]+)'
		re_expr+='[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)'
		re_expr+='[[:space:]]+([[:digit:]]+)%[[:space:]]+([._/[:alnum:]-]+)$'
		local var_free=""

		var_df_warn_b=$( parseSize "${var_df_warn}" ) || return 2
		[ ${var_df_warn_b} -eq 0 ] && return 0

		var_df_str="$( ${var_ussh:-eval} \
			"${df_bin} -B1 '${var_mp}'" | ${grep_bin} "${var_mp}" )"

		if ! [[ "${var_df_str}" =~ ${re_expr} ]]; then

			logCmd 2 "${str_p_dfpf} '${var_df_str}'"
			return 1
		fi

		var_free="${BASH_REMATCH[4]}"

		if [ ${var_free} -lt ${var_df_warn_b} ]; then

			echo "${var_free}/${var_df_warn_b}"
			return 1
		fi

		return 0
	}


	prepImagePart() {

		local -a var_plst=( ${src_path['plst']} )
		local var_part=""
		local var_upart=""
		local var_ussh="${src_path['ussh']}"
		local var_mbase=""
		local -a var_elist=()
		local var_partn=""
		local var_estr=""
		local -i fn=0
		local var_lfile="${opts['lib_path']}/mount.lst"
		local -a var_df_warn=( ${opts['src_df_warn']//[,;]/ } )
		local tmp_data=""

		# test for binaries locally or remote
		checkBinSave "ntfstools" "${ntfstools_bin_lst[*]}" \
			"${var_ussh}" || return 1

		# prepare partitions
		for var_part in "${var_plst[@]}"; do

			var_upart="${src_path['uhost']}${var_part}"
			var_mbase="${opts['src_mnt_base']/%\*/${var_part##*/}}"

			# load eval list file
			# file format:
			#   <pn> <eval string>
			if [ -n "${opts['eval_list_file']}" ] && \
			   [ -f "${opts['eval_list_file']}" ]; then

				logCmd 4 "${str_p_src_lel} '${opts['eval_list_file']}'"
				var_elist=()
				while read -r var_partn var_estr; do

					if [ "${var_partn}" == "${var_part: -1}" ]; then
						var_elist+=( "${var_estr}" )
					fi
				done < "${opts['eval_list_file']}"
			fi

			# if the evaluation list is empty, we have nothing
			# to do for the partition
			if [ ${#var_elist[@]} -eq 0 ]; then

				logCmd 4 "${str_p_src_np} '${var_part}'"
				continue
			fi

			# devel log: eval list
			logCmd 5 "${str_p_src_el}"
			for fn in "${!var_elist[@]}"; do
				logCmd 5 "[${fn}]: '${var_elist[${fn}]}'"
			done

			# mount partition within device
			tmp_data=$(mountDev "${var_part}" \
				"${var_mbase}" \
				"${var_ussh}" \
				"${opts['src_mnt_opts']}" )

			case ${?} in

				0)
					# save mount list
					mntSave "${var_upart}"
				;;

				1)
					# warn if partition is expected to be unmounted
					if ${grep_bin} -q -E "^${var_upart}$" \
					   "${var_lfile}" 2>/dev/null; then

						logCmd 2 "${str_p_src_am} '${var_upart}'"
					fi
				;;

				7)
					# nothing further to process as we have raw data
					continue
				;;

				*)
					return 1
				;;
			esac

			# processing eval list on partition
			logCmd 3 "${str_p_src_pel} '${var_part}'"

			tmp_data=$( checkDF "${var_mbase}" \
				"${var_df_warn[$(( ${var_part: -1} - 1 ))]:-0}" \
				"${var_ussh}" )

			if [ ${?} -ne 0 ]; then
				logCmd 2 "${str_p_wth} '${var_ussh:+${var_ussh}:}${var_part}' ${tmp_data}"
			fi

			for fn in "${!var_elist[@]}"; do

				var_estr="${var_elist[${fn}]//<basepath>/${var_mbase}/}"
				logCmd 4 "[${fn}]: '${var_estr}'"

				#if ! var_err=$( ${var_ussh:-eval} \
				#     "${var_estr}" 2>&1 ); then
				#
				#	logCmd 1 "${str_p_src_ee} ${var_err}"
				#	return 1
				#fi
				var_err=$( ${var_ussh:-eval} "${var_estr}" 2>&1 )
				logCmd 5 "[out]: '${var_err}'"
			done

			if ! mntRemove "${var_upart}" "${var_ussh}"; then
				return 1
			fi
		done

		return 0
	}


	# mount source device
	# provides: src_path[udev]
	#   (via mountDev): src_path[source,target,fstype,fsopts]
	# src_path[source,target,fstype,fsopts] can already exist from queryMount
	# of the origin of a snapshot
	# uses: src_path[uhost,mdev,ussh]
	#       opts[src_mnt_opts, skip_unmounted_src]
	mountSrc() {

		local -i rc=0
		local fn=""
		local tmp_data=""

		src_path['udev']="${src_path['uhost']}${src_path['mdev']}"

		# mount source
		tmp_data=$(mountDev "${src_path['mdev']}" \
			"${opts['src_mnt_base']/%\*/$( getName 2 0 0 )}" \
			"${src_path['ussh']}" \
			"${src_path['fsopts']:-${opts['src_mnt_opts']}}" \
			"${src_path['fstype']:-${opts['src_fs_type']}}" \
			 $(( ${opts['snap_size']:+0*}${opts['skip_unmounted_src']} )) )

		rc=${?}
		[ ${rc} -gt 2 ] && return 1

		for fn in "${mnt_lst[@]}"; do
			unset src_path["${fn}"]
		done
		eval "src_path+=( ${tmp_data} )"

		# log source mount options
		logCmd 5 "${str_mnt_opts}"
		for fn in "${mnt_lst[@]}"; do
			logCmd 5 "src_path['${fn}']='${src_path[${fn}]}'"
		done

		case ${rc} in

			0)
				# save mount list
				mntSave "${src_path['udev']}"
			;;

			1)
				# warn if device is expected to be unmounted
				if ${grep_bin} -q -E "^${src_path['udev']}$" \
				   "${opts['lib_path']}/mount.lst" 2>/dev/null; then

					logCmd 2 "${str_p_src_am} '${src_path['udev']}'"
				fi
			;;

			2)
				# skip unmounted devices, if configured to do so
				logCmd 3 "${str_p_src_skum} '${src_path['udev']}'"
				return 1
			;;
		esac

		src_path['base']="${src_path['target']}"

		return 0
	}


	# prepare source path
	# options: lvs data string
	# provides: src_path[mdev,adev,base(type: dir only)], src_lvs
	#   (via queryMount temp for snapshots later via mountSrc):
	#           src_path[udev,base(all other types),source,target,fstype,fsopts]
	# uses: src_path[type,ext,ussh,dpath], lvs_lst
	#       opts[excludes,snap_size,snap_suffix,src_mnt_base,src_mnt_opts
	#            skip_unmounted_src,lib_path,src_relpath]
	prepareSrc() {

		local lvs_s="${1}"
		local -a lvs_a
		local tmp_data=""
		local -a var_mlst=()
		local find_expr="${find_bin} -L /dev -maxdepth 4 -mount -samefile <filepattern> 2>/dev/null"
		local grep_expr="${grep_bin} -P '^/dev/mapper/((?!-(cow|real)$).)+$'"
		local var_snaps="${opts['snap_size']}"
		local -a var_mlst=()
		local -a var_df_warn=( ${opts['src_df_warn']//[,;]/ } )
		local tmp_data=""

		logCmd 4 "${str_p_src}"
		case "${src_path['type']}" in

			vg|lv)
				# populate ${src_lvs}
				lvs_a=( ${lvs_s//,/ } )
				for fn in "${!lvs_lst[@]}"; do
					src_lvs[${lvs_lst[${fn}]}]="${lvs_a[${fn}]}"
				done

				# skip empty VG
				if [ -z "${src_lvs['volume']}" ]; then
					logCmd 2 "${str_p_src_sevg} '${src_lvs['group']}'"
					continue
				fi

				src_path['mdev']="/dev/${src_lvs['group']}/${src_lvs['volume']}"
				src_path['adev']="${src_path['uhost']}${src_path['mdev']}"
				[[ "${src_path['type']}" =~ ^vg$ ]] && logCmd 3 "${str_p_src_slvc} '${src_lvs['volume']}'"
				logCmd 4 "${str_p_src_plv} '${src_path['mdev']}'"

				# log lvm data
				logCmd 5 "${str_p_src_lvs}"
				for fn in "${!src_lvs[@]}"; do
					logCmd 5 "src_lvs['${fn}']='${src_lvs[${fn}]}'"
				done

				# processing exclude list
				if [ "${src_path['type']}" == "vg" ] && [ -n "${opts['excludes']}" ]; then

					for fn in ${opts['excludes']//[,;]/ }; do
						if [[ "${src_lvs['volume']}" =~ ^${fn}$ ]]; then
							logCmd 3 "${str_p_src_sel} '${src_lvs['volume']}'"
							return 1
						fi
					done
				fi

				# do we skip inactive volumes ?
				if ! [[ "${src_lvs['vattr']}" =~ ^....a ]]; then

					if [ ${opts['lv_src_act']} -eq 1 ]; then

						lvChange "${src_path['mdev']}" 1 ${opts['lv_src_al']} \
							 ${opts['lv_src_ae']} 0 "${src_path['ussh']}"
						[ ${?} -ne 0 ] && return 1

						# save to activation list
						actSave "${src_path['adev']}"
					else
						logCmd $( [ ${opts['warn_inact_lv_src']} -eq 1 ] && echo 2 || echo 3 ) \
						       "${str_p_src_siv} '${src_path['mdev']}'"
						return 1
					fi
				fi

				# do not process snapshots
				if [[ "${src_lvs['vattr']}" =~ ^[sS] ]]; then
					logCmd 4 "${str_p_src_ssnap} '${src_lvs['volume']}'"
					return 1
				fi

				# do not process thinpools
				if [[ "${src_lvs['vattr']}" =~ ^t.....t ]]; then
					logCmd 4 "${str_p_src_stpool} '${src_lvs['volume']}'"
					return 1
				fi

				# do not process swap volumes
				if [[ $( ${src_path['ussh']:-eval} "${file_bin} -sL '${src_path['mdev']}'" ) =~ swap\ file ]]; then
					logCmd 4 "${str_p_src_swp} '${src_path['mdev']}'"
					return 1
				fi

				# we create snapshots if we have a snapshot size
				if [ -n "${var_snaps}" ]; then

					# check that snapshot suffix is not empty
					if [ -z "${opts['snap_suffix']}" ]; then
						logCmd 1 "${str_snap_sfxe}"
						return 1
					fi

					# get mount opts. of original volume, if mounted, to use later
					for fn in "${mnt_lst[@]}"; do
						unset src_path["${fn}"]
					done
					tmp_data=$(queryMount "s" "${src_path['mdev']}" "${src_path['ussh']}")
					if [ ${?} -eq 0 ]; then
						eval "src_path+=( ${tmp_data} )"
						logCmd 4 "${str_p_src_oopts} '${src_path['fsopts']}'"
					fi

					src_path['mdev']+="${opts['snap_suffix']}"

					# check if there isn't still an old snapshot
					if ${src_path['ussh']:-eval} "${test_bin} -b '${src_path['mdev']}'"; then
						logCmd 1 "${str_snap_se} '${src_path['mdev']}'"
						return 1
					fi

					# do we have a thin volume?
					if [[ "${src_lvs['vattr']}" =~ ^V.....t ]]; then
						var_snaps="T"
					fi

					# create snapshot
					snapCreate "${src_path['mdev']}" "${var_snaps}" "${src_path['ussh']}"
					[ ${?} -ne 0 ] && return 1
					# from here on we need to cleanup our mess

					# we have to activate thin snapshots
					if [[ "${src_lvs['vattr']}" =~ ^V.....t ]]; then
						lvChange "${src_path['mdev']}" 1 1 1 1 "${src_path['ussh']}"
						if [ ${?} -ne 0 ]; then Cleanup && return 1; fi
					fi
				fi
			;;

			"bd")
				src_path['mdev']="${src_path['dpath']}"
				logCmd 4 "${str_p_src_pbd} '${src_path['mdev']}'"
			;;

			"dir")
				src_path['base']="${src_path['dpath']}"
				logCmd 4 "${str_p_src_pd} '${src_path['base']}'"
			;;

			"tp")
				logCmd 1 "${str_p_src_tp} '${src_path['full']}'"
				return 1
			;;

			"cifs")
				src_path['mdev']="${src_path['dpath']}"
				# append source mount options, is available to current opts
				src_path['fsopts']+="${opts['src_mnt_opts']:+${src_path['fsopts']:+,}${opts['src_mnt_opts']}}"
				logCmd 4 "${str_p_src_pcifs} '${src_path['mdev']}'"
			;;
		esac

		if [ "${opts['method']}" == "rsync" ]; then

			# check if we have to mount something
			if [ "${src_path['type']}" != "dir" ]; then

				if ! mountSrc; then Cleanup && return 1; fi

				tmp_data=$( checkDF "${src_path['base']}" \
					"${var_df_warn[0]:-0}" \
					"${src_path['ussh']}" )

				if [ ${?} -ne 0 ]; then
					logCmd 2 "${str_p_wth} '${src_path['ussh']:+${src_path['ussh']}:}${src_path['dpath']}' ${tmp_data}"
				fi
			fi

			# append relative path
			if [ "${src_path['type']}" != "vg" ] && [ -n "${opts['src_relpath']}" ]; then

				opts['src_relpath']="${opts['src_relpath']/%\/}"
				opts['src_relpath']="${opts['src_relpath']/#\/}"

				src_path['base']+="/${opts['src_relpath']}"

				# refuse to continue if src doesn't exist
				if ! ${src_path['ussh']:-eval} "${test_bin} -d '${src_path['base']}'"; then
					logCmd 1 "${str_p_src_path_ne} '${src_path['base']}'"
					Cleanup && return 1
				fi
			fi

			# refuse to continue with an empty base
			if [ -z "${src_path['base']}" ]; then
				logCmd 1 "${str_p_src_ebase}"
				Cleanup && return 1
			fi

			tmp_data="${src_path['source']:+ (${src_path['source']})${opts['src_relpath']:+/${opts['src_relpath']}}}"
			logCmd 3 "${str_p_src_path} '${src_path['uhost']}${src_path['base']}${tmp_data}'"
		else
			if [ "${src_path['type']}" == "dir" ]; then
				logCmd 1 "${str_p_src_uibdir}"
				Cleanup && return 1
			fi

			# get the mapper path of the device
			# we have to discard '*-cow$' mapper links from search
			# result due to some device mapper misbehaviour
			IFS=$'\x0a'
			var_mlst=( $( IFS="${IFS_save}" ; ${src_path['ussh']:-eval} \
				"${find_expr/<filepattern>/${src_path['mdev']}} |\
				${grep_expr}" ) )
			IFS="${IFS_save}"

			if [ ${#var_mlst[@]} -gt 1 ]; then
				# fail if we found more than one in /dev/mapper
				logCmd 1 "${str_q_path_mpe} '${src_path['mdev']}' (${#var_mlst[@]})"
				logCmd 1 "${var_mlst[*]}"
				Cleanup && return 1
			elif [ ${#var_mlst[@]} -eq 1 ]; then
				src_path['mapper']="${var_mlst[0]}"
			else
				logCmd 1 "${str_q_path_mpna} '${src_path['mdev']}'"
				Cleanup && return 1
			fi

			# activate partition table on device
			# and get partition list
			if ! src_path['plst']="$( ptableSave 'src_path' )"; then
				Cleanup && return 1
			fi

			# prepare partitions on device for backup (eval command list)
			if ! prepImagePart; then
				Cleanup && return 1
			fi
		fi

		return 0
	}


	# provides: dst_path[base,udev]
	#        base = mount target
	#        udev = uhost and mdev (final device): 'user@host:devicepath'
	mountDst() {

		local var_fsopts=""
		local -i rc=0
		local fn=""

		logCmd 4 "${str_p_dst_pdev} '${dst_path['mdev']}'"
		dst_path['udev']="${dst_path['uhost']}${dst_path['mdev']}"

		if [ "${dst_path['fstype']}" == "cifs" ] || \
		   [ "${src_path['fstype']}" == "cifs" ]; then

			var_fsopts="${dst_path['fsopts']:-${opts['dst_mnt_opts']}}"

		elif [ "${dst_path['fstype']}" == "${src_path['fstype']}" ]; then

			var_fsopts="${src_path['fsopts']:-${opts['dst_mnt_opts']}}"
		else
			var_fsopts="${opts['dst_mnt_opts']}"

		fi

		# TODO: a possibility to force dst_mnt_opts
		# mount destination
		tmp_data=$(mountDev "${dst_path['mdev']}" \
			"${opts['dst_mnt_base']/%\*/$( getName 2 0 0 )}" \
			"${var_usshd}" \
			"${var_fsopts}" \
			"${dst_path['fstype']:-${opts['dst_fs_type']}}")

		rc=${?}
		[ ${rc} -gt 1 ] && return 1

		for fn in "${mnt_lst[@]}"; do
			unset dst_path["${fn}"]
		done
		eval "dst_path+=( ${tmp_data} )"

		# log destination mount options
		logCmd 5 "${str_mnt_opts}"
		for fn in "${mnt_lst[@]}"; do
			logCmd 5 "dst_path['${fn}']='${dst_path[${fn}]}'"
		done

		case ${rc} in

			0)
				# save mount list
				mntSave "${dst_path['udev']}"
			;;

			1)
				# skip devices, mounted at an unexpected target,
				# if configured to do so
				if [ ${opts['skip_mounted_dst']} -eq 1 ] && \
				   [ "${dst_path['target']}" != "${opts['dst_mnt_base']}" ]; then

					logCmd 1 "${str_p_dst_skm} '${dst_path[udev]} -> ${dst_path[target]}'"
					return 1
				fi

				# warn if device is expected to be unmounted
				if ${grep_bin} -q -E "^${dst_path['udev']}$" \
				   "${opts['lib_path']}/mount.lst" 2>/dev/null; then

					logCmd 2 "${str_p_dst_am} '${dst_path[udev]}'"
				fi
			;;
		esac

		dst_path['base']="${dst_path['target']}"

		return 0
	}


	# prepare destination path
	# provides: dst_path[mdev,adev,base,udev], (dst_path[source,target,fstype,fsopts])
	#           mdev = final dst device
	#           adev = activation dst device (with remote host)
	# need: dst_path[type,ext,ussh,dpath,lvs,path], src_path[fsopts], src_lvs, lvs_lst
	#       opts[dst_mnt_base,dst_mnt_opts,lv_prefix,lv_suffix,
	#            force_lv_prefix,force_lv_suffix,skip_mounted_dst,lib_path]
	prepareDst() {

		local tmp_data=""
		local var_name=""
		local var_group=""
		local -i var_size=0
		local var_ssize=""
		local var_stripes="-i ${opts['lv_stripes']:-1}"
		local var_pref="${opts['lv_prefix']}"
		local var_suff="${opts['lv_suffix']}"
		local var_usshs="${src_path['ussh']}"
		local var_usshd="${dst_path['ussh']}"
		local lvs_s=""
		local -i rc=0
		local -a lvs_a
		local -A dst_lvs
		local find_expr="${find_bin} -L /dev -maxdepth 4 -mount -samefile <filepattern> 2>/dev/null"
		local grep_expr="${grep_bin} -P '^/dev/mapper/((?!-(cow|real)$).)+$'"
		local -a var_mlst=()
		local -a var_df_warn=( ${opts['dst_df_warn']//[,;]/ } )
		local tmp_data=""

		logCmd 4 "${str_p_dst}"

		case "${dst_path['type']}" in

			vg|tp|lv)
				# calculate volume size
				if [ -n "${src_lvs['vsize']}" ]; then

					var_size=$( ${bc_bin} <<< "scale=0; (${src_lvs['vsize']} * ${opts['lv_oversize']//[,]/.}) / 4096 * 4096" )
				fi

				# for dst type (vg|tp) the dst volume name
				# derives from the src volume name 
				if [[ "${dst_path['type']}" =~ ^(vg|tp)$ ]]; then

					# prepend/append prefix and/or suffix to lv
					# use prefix if src or dst is remote or if forced, if lv_prefix is set
					if [ -n "${var_pref}" ] && ( [ ${opts['force_lv_prefix']} -eq 1 ] || \
					   ( [ -n "${var_usshs}" ] || [ -n "${var_usshd}" ] ) \
					   && [ ${opts['add_hname']} -eq 1 ] ); then

						var_name+="${var_pref/<hostname>/$( ${var_usshs:-eval} "${hostname_bin}" )}"
					fi

					var_name+="${src_lvs['volume']}"

					# use suffix if src/dst VG are the same or if forced
					if [ "${dst_path['lvs']/%,*}" == "${src_lvs['group']}" ] && \
					   [ -z "${var_usshs}" ] && [ -z "${var_usshd}" ] || \
					   [ ${opts['force_lv_suffix']} -eq 1 ]; then

						var_name+="${var_suff}"
					fi
				fi

				logCmd 4 "${str_p_dst_slv} '${var_name:-${dst_path['dpath']}}'"

				# figure out if we have to create a volume in a VG or TP
				# first scan VG lvs data for dst volume name
				for lvs_s in ${dst_path['lvs']}; do

					# populate ${dst_lvs}
					lvs_a=( ${lvs_s//,/ } )
					for fn in "${!lvs_lst[@]}"; do
						dst_lvs[${lvs_lst[${fn}]}]="${lvs_a[${fn}]}"
					done

					[[ "${dst_path['type']}" =~ ^(vg|tp)$ ]] || break
					[[ "${src_lvs['vattr']}" =~ ^[sS] ]] && continue
					[ "${var_name}" == "${dst_lvs['volume']}" ] && break
					dst_lvs['volume']=""
				done

				# we came up empty in dst VG or TP, so we create one
				if [ -z "${dst_lvs['volume']}" ]; then

					# we have to create the lv

					# do we use a thin pool volume instead of a group?
					if [ "${dst_path['type']}" == "tp" ]; then

						var_group="${dst_path['dpath']}"
						var_stripes=""
						var_ssize="-V ${var_size}b"
					else
						var_group="${dst_lvs['group']}"
						var_ssize="-L ${var_size}b"
					fi

					lvCreate "${var_name}" "${var_group}" "${var_ssize}" \
						 "${var_stripes}" \
						 "${var_usshd}"

					[ ${?} -ne 0 ] && return 1

					dst_lvs['volume']="${var_name}"
					dst_lvs['vsize']="${var_size}"
					dst_lvs['gfree']="$(( ${dst_lvs['gfree']} - ${dst_lvs['vsize']} ))"

					dst_path['mdev']="/dev/${dst_lvs['group']}/${dst_lvs['volume']}"
					dst_path['adev']="${dst_path['uhost']}${dst_path['mdev']}"

					if ! containsElement "${opts['method']}" "${clone_methods[@]}" || \
					   [ ${opts['disk2disk']} -eq 0 ]; then

						fsCreate "${dst_path['mdev']}" \
							 "${opts['dst_fs_type']:-${src_path['fstype']}}" \
							 "${opts['dst_fs_opts']}" \
							 "${var_usshd}"
					fi

					[ ${?} -ne 0 ] && return 1

					# save to activation list
					if [ ${opts['lv_dst_act']} -eq 1 ]; then

						actSave "${dst_path['adev']}"
					fi
				else
					dst_path['mdev']="/dev/${dst_lvs['group']}/${dst_lvs['volume']}"
					dst_path['adev']="${dst_path['uhost']}${dst_path['mdev']}"

					# do we skip inactive volumes ?
					if ! [[ "${dst_lvs['vattr']}" =~ ^....a ]]; then

						if [ ${opts['lv_dst_act']} -eq 1 ]; then

							lvChange "${dst_path['mdev']}" 1 ${opts['lv_dst_al']} \
							         ${opts['lv_dst_ae']} 0 "${var_usshd}"
							[ ${?} -ne 0 ] && return 1

							# save to activation list
							actSave "${dst_path['adev']}"
						else
							logCmd 2 "${str_p_dst_siv} '${dst_path['mdev']}'"
							return 1
						fi
					fi

					# resize logical volumes
					if [ ${opts['lv_resize']} -eq 1 ] && [ ${var_size} != 0 ] && \
					   [ ${var_size} -gt ${dst_lvs['vsize']} ]; then

						# test destination lv size
						lvResize "${dst_path['mdev']}" \
							"${var_size}" "${var_usshd}"
						[ ${?} -ne 0 ] && return 1

						# resize fs
						fsResize "${dst_path['mdev']}" \
							"${var_usshd}"
						[ ${?} -ne 0 ] && return 1
					fi
				fi
			;;

			"bd")
				dst_path['mdev']="${dst_path['dpath']}"
			;;

			"dir")
				dst_path['base']="${dst_path['path']}"
				logCmd 4 "${str_p_dst_pdir} '${dst_path['base']}'"
			;;

			"cifs")
				dst_path['mdev']="${dst_path['dpath']}"
				dst_path['fsopts']+="${opts['dst_mnt_opts']:+${dst_path['fsopts']:+,}${opts['dst_mnt_opts']}}"
				logCmd 4 "${dst_p_src_pcifs} '${dst_path['mdev']}'"
			;;
		esac

		if ! containsElement "${opts['method']}" "${clone_methods[@]}" || \
		   [ ${opts['disk2disk']} -eq 0 ]; then

			# check if we have to mount something
			if [ "${dst_path['type']}" != "dir" ]; then

				mountDst || return 1

				tmp_data=$( checkDF "${dst_path['base']}" \
					"${var_df_warn[0]:-0}" \
					"${var_usshd}" )

				if [ ${?} -ne 0 ]; then
					logCmd 2 "${str_p_wth} '${var_usshd:+${var_usshd}:}${dst_path['dpath']}' ${tmp_data}"
				fi
			fi

			# append relative path
			if [[ ! "${dst_path['type']}" =~ ^(vg|tp)$ ]] && \
			   [ -n "${opts['dst_relpath']}" ]; then

				opts['dst_relpath']="${opts['dst_relpath']/%\/}"
				opts['dst_relpath']="${opts['dst_relpath']/#\/}"

				dst_path['base']+="/${opts['dst_relpath']}"
				pathCreate "${dst_path['base']}" "${var_usshd}"
				[ ${?} -ne 0 ] && return 1
			fi

			# refuse to continue with an empty base
			if [ -z "${dst_path['base']}" ]; then
				logCmd 1 "${str_p_dst_ebase}"
				return 1
			fi

			tmp_data="${dst_path['source']:+ (${dst_path['source']})${opts['dst_relpath']:+/${opts['dst_relpath']}}}"
			logCmd 3 "${str_p_dst_path} '${dst_path['uhost']}${dst_path['base']}${tmp_data}'"
		else
			# get the mapper path of the device
			# we have to discard '*-cow$' mapper links from search
			# result due to some device mapper misbehaviour
			IFS=$'\x0a'
			var_mlst=( $( IFS="${IFS_save}" ; ${var_usshd:-eval} \
				"${find_expr/<filepattern>/${dst_path['mdev']}} |\
				${grep_expr}" ) )
			IFS="${IFS_save}"

			if [ ${#var_mlst[@]} -gt 1 ]; then
				# fail if we found more than one in /dev/mapper
				logCmd 1 "${str_q_path_mpe} '${dst_path['mdev']}' (${#var_mlst[@]})"
				logCmd 1 "${var_mlst[*]}"
				return 1
			elif [ ${#var_mlst[@]} -eq 1 ]; then
				dst_path['mapper']="${var_mlst[0]}"
			else
				logCmd 1 "${str_q_path_mpna} '${dst_path['mdev']}'"
				return 1
			fi

			ptableDeactivate "dst_path"
		fi

		return 0
	}


	# options: naming rule
	# rule: 0 = name
	#       1 = mount path
	#       2 = path or device
	# uses: dst_path[uhost,type], src_path[ussh,uhost,name,target,path,type]
	#       opts[src_mnt_base,root_name,add_hname,src_relpath], src_lvs[group,volume]
	getName() {

		local var_rule=${1}
		local var_add_hname=${2:-${opts['add_hname']}}
		local var_add_rpath=${3:-${opts['add_rpath']}}
		local var_name=""

		# ensure useful names to backup a VG to something else
		# than an other VG
		if [[ "${src_path['type']}" =~ ^vg|tp$ ]] && \
		   [[ ! "${dst_path['type']}" =~ ^vg|tp$ ]] && \
		   [ ${var_rule} -eq 0 ]; then

			logCmd 3 "${str_p_dst_nr_vgw}"
			var_rule=2
		fi

		# ensure a useful name rule for image backups
		if [ "${opts['method']}" != "rsync" ] && \
		   [ ${opts['name_rule']} -eq 1 ]; then

			var_rule=0
		fi

		# name from src extension, can be empty
		if [ ${var_rule} -eq 0 ]; then

			if [ "${opts['method']}" == "rsync" ]; then

				echo "${opts['name']}"
				return 0
			else
				# ensure a useful name for image backups
				if [ -n "${opts['name']}" ]; then

					echo "${opts['name']}"
					return 0
				else
					if [ -n "${opts['xen_dom']}" ]; then

						echo "${opts['xen_dom']}"
						return 0

					else
						var_rule=2
					fi
				fi
			fi
		fi

		# prepend sources hostname to backup name
		if ( [ -n "${src_path['uhost']}" ] || [ -n "${dst_path['uhost']}" ] ) && \
		   [ ${var_add_hname} -eq 1 ]; then

			var_name="${src_path['uhost']/#*@}"
			var_name=${var_name:-$( ${src_path['ussh']:-eval} "${hostname_bin}" ):}
		fi

		# use mount path
		if [ ${var_rule} -eq 1 ]; then

			# FIXME: this might be suitable if src_mnt_base is defined for
			#        each backup, but then we have even to exclude vg from
			#        this rule
			if [ "${src_path['target']}" == "${opts['src_mnt_base']}" ] || \
			   [ -z "${src_path['target']}" ]; then

				logCmd 3 "${str_p_dst_nr_sw} 2"
				var_rule=2

			elif [ "${src_path['target']}" == "/" ]; then
				var_name+="${opts['root_name']}"
			else
				var_name+="${src_path['target']/#\/}"
			fi
		fi

		# use src path or device name)
		if [ ${var_rule} -eq 2 ]; then

			if [ "${src_path['type']}" == "bd" ]; then
				var_name+="${src_path['path']/#\/}"
			elif [[ "${src_path['type']}" =~ ^(lv|vg)$ ]]; then
				var_name+="${src_lvs['group']}/${src_lvs['volume']}"
			elif [ "${src_path['path']}" == "/" ]; then
				var_name+="${opts['root_name']}"
			elif [ "${src_path['type']}" == "cifs" ]; then
				var_name+="${src_path['path']##//}"
			else
				var_name+="${src_path['path']##/}"
			fi
		fi

		if [ -n "${opts['src_relpath']}" ] && [ ${var_add_rpath} -eq 1 ]; then

			var_name+=":${opts['src_relpath']}"
		fi

		var_name="${var_name//-/--}"
		var_name="${var_name//\//-}"
		var_name="${var_name//./..}"
		var_name="${var_name// /.}"
		var_name="${var_name//_/__}"
		var_name="${var_name//:/_}"

		echo "${var_name}"
		return 0
	}


	# check for a no-space-lock file
	# uses: dst_path[ussh,type,base,source,target,uhost], mnt_lst
	# returns: mount base of the backup destination
	testLock() {

		local var_path=""
		local -a var_mtab
		local -A var_mnt
		local fn=""
		local f2=""
		local re_expr="$( getMntRe "${dst_path['ussh']}" )"
		local IFS_save="${IFS}"
		local tmp_data=""
		checkBinSave "mountpoint" "${mntpt_bin_lst[*]}" "${dst_path['ussh']}" 0 1
		local -i var_mp=${?}

		if [ ${var_mp} -ne 0 ]; then
			# get mount list
			IFS=$'\x0a'
			var_mtab=( $( IFS="${IFS_save}" ; ${dst_path['ussh']:-eval} "${mount_bin}" ) )
			IFS="${IFS_save}"
		fi

		if [ "${dst_path['type']}" == "dir" ]; then

			var_path="${dst_path['base']}"
			until [ -z "${var_path}" ]; do

				if [ ${var_mp} -eq 0 ]; then
					${dst_path['ussh']:-eval} "${mountpoint_bin} -q '${var_path:-/}'" && break
				else
					for fn in "${var_mtab[@]}"; do

						# match each mount list element
						if ! [[ "${fn}" =~ ${re_expr} ]]; then
							# parsing error
							logCmd 1 "${str_mnt_pe} '${fn}'"
							return 2
						fi
						# assign var_mnt[source,target,fstype,fsopts]
						var_mnt=()

						for f2 in "${!mnt_lst[@]}"; do
							var_mnt[${mnt_lst[${f2}]}]+="${BASH_REMATCH[${f2}+1]}"
						done

						if [ "${var_mnt['target']}" == "${var_path:-"/"}" ]; then
							break 2
						fi
					done
				fi
				var_path="${var_path%/*}"
			done
			logCmd 4 "${str_p_dst_ddm} '${var_path:-"/"}'"
		else
			var_path="${dst_path['target']}"
		fi

		if [ -z "${var_path}" ]; then

			logCmd 2 "${str_p_dst_rfw}"
		fi

		echo -n "${var_path:-"/"}"

		if ${dst_path['ussh']:-eval} "${test_bin} -e '${var_path}/nospace_lock'"; then

			tmp_data="${dst_path['source']:+ (${dst_path['source']})}"
			logCmd 1 "${str_p_dst_ns} '${dst_path['uhost']}${var_path}${tmp_data}'"
			return 1
		fi

		return 0
	}


	# find last incremental backup, removes old ones, provides date
	# extendion and destination link
	# provides: dst_path[inc,dlnk]
	# uses: dst_path[base,name,ussh], opts[bu_sets,bu_set,off_days,proc_suffix,inc_suffix]
	#       date_str
	testIncremental() {

		local var_set=${opts['bu_set']}
		local var_sets=( ${opts['bu_sets']//[,;]/ } )
		local var_base="${dst_path['base']}"
		local var_name="${dst_path['name']}"
		local var_full="${var_base}/"
		local var_inc=""
		if [ -n "${var_name}" ]; then
			var_full+="${var_name}_"
			var_inc+="_"
		fi
		local re_expr0="${var_full}[0-9]{4}([-|_][0-9]{2}){5}_[0-9]+"
		local re_expr1="^(${re_expr0})${opts['proc_suffix']}$"
		local re_expr2="^(${re_expr0})${opts['inc_suffix']}$"
		local find_expr1="${find_bin} '${var_base}/' -maxdepth 1 "
		find_expr1+="-regextype posix-egrep -regex '^${re_expr0}"
		find_expr1+="(${opts['proc_suffix']}|${opts['inc_suffix']})?$'"
		local sed_expr='s,^.+_?([0-9]{4}-[0-9]{2}-[0-9]{2})_'
		sed_expr+='([0-9]{2})-([0-9]{2})-([0-9]{2}).+$,\1 \2:\3:\4,'
		local var_ussh="${dst_path['ussh']}"
		local -a var_blst=()
		local -i var_expd=0
		local -i var_exps=0
		local -i c=0
		local var_b=""
		local -i var_bx=0
		local var_ipb=""
		local var_last=""
		local IFS_save="${IFS}"

		# check for incremental backups
		if [ ${#var_sets[@]} -eq 0 ]; then

			logCmd 3 "${str_inc_ni}"
			return 0
		fi

		# check validity of bu_set
		if [ ${var_set} -ge ${#var_sets[@]} ] || \
		   [ ${var_set} -lt 0 ]; then

			logCmd 1 "${str_inc_set_nc} '${var_set}'"
			return 1
		fi

		# test some binaries
		checkBinSave "incl" "${incl_bin_lst[*]}" || return 1
		if [ -n "${var_ussh}" ]; then
			checkBinSave "incr" "${incr_bin_lst[*]}" "${var_ussh}" || return 1
		fi

		# set destination for incremental backup
		var_inc+="${date_str}_${var_set}"
		logCmd 3 "${str_inc_dst} '${var_full}${date_str}_${var_set}'"
		dst_path['inc']="${var_inc}"

		logCmd 4 "${str_inc_s} '${var_base}'"

		# search for previous incremental backups
		IFS=$'\x0a'
		var_blst=( $( IFS="${IFS_save}" ; ${var_ussh:-eval} \
		              "${find_expr1}" 2>/dev/null | ${sort_bin} ) )
		IFS="${IFS_save}"
		if [ ${#var_blst[@]} -eq 0 ]; then
			logCmd 4 "${str_inc_np}"
			return 0
		fi

		for (( fn=${#var_blst[@]}-1 ; fn>=0 ; fn-- )); do

			# log existing incremental backups
			logCmd 5 "var_blst[$fn]: ${var_blst[${fn}]}"

			# warn about and rename 'in progress' backups
			if [[ "${var_blst[${fn}]}" =~ ${re_expr1} ]]; then

				logCmd 2 "${str_inc_dst_ip} '${var_blst[${fn}]}'"
				pathRename "${var_blst[${fn}]}" \
					"${BASH_REMATCH[1]}${opts['inc_suffix']}" "${var_ussh}"
				var_blst[${fn}]="${BASH_REMATCH[1]}${opts['inc_suffix']}"
				continue
			fi

			# set last complete backup as link-dest for incremental backup
			if ! [[ "${var_blst[${fn}]}" =~ ${re_expr2} ]] && \
			   [ -z "${var_last}" ]; then

				var_last="${var_blst[${fn}]}"
				#dst_path['dlnk']="--link-dest=../${var_last##*/}/"
				dst_path['dlnk']="${var_last}/"
				if [ "${opts['method']}" == "rsync" ]; then
					logCmd 3 "${str_inc_rev_pdst} '${var_last}'"
				fi
			fi
		done

		# add off-days, if any, to the expire time; we just count work-days
		# if the current backup sets expire days is 0, we set var_exps to 0
		# so backups never expire
		var_expd=${var_sets[${var_set}]}
		if [ ${var_expd} -eq 0 ]; then
			var_exps=0
		else
			logCmd 4 "${str_inc_ao}"

			for ((c=1; c<=${var_expd}; c++)); do

				# logCmd 5 "${c}: $(${date_bin} -d "-${c} day" +%a)"
				if [[ "${opts['off_days']}" =~ $(${date_bin} -d "-${c} day" +%a) ]]; then
					var_expd+=1
					# logCmd 5 "+++"
				fi
			done
			var_exps=$( ${date_bin} --date "${var_expd} days ago" +%s )
		fi

		# find incremental backups to remove
		logCmd 4 "${str_inc_ro} '${var_name:-"<no name>"}'"
		for var_b in "${var_blst[@]}"; do

			# just in case
			[ -z "${var_b}" ] || [ "${var_b}" == "/" ] && continue
			var_bx=$(${date_bin} --date "$(echo ${var_b} | ${sed_bin} -r -e "${sed_expr}")" +%s)

			if [ "${var_b: -1}" == "${var_set}" ] && \
			   [ $(( ${var_bx} + 60 )) -le ${var_exps} ]; then

				if [ "${var_b}" == "${var_last}" ]; then

					logCmd 2 "${str_inc_clean_r} '${var_b}'"
					continue
				fi

				Remove "${var_b}" "${var_ussh}" 0 1
			fi
		done

		return 0
	}


	# build rsync depht filter
	getDepthFilter() {

		local var_depth=${1}
		local var_fstr=""
		local var_fdepth=""
		local -i count=0

		[ ${var_depth} -eq 0 ] && return

		logCmd 4 "${str_bu_sdepth} ${var_depth}"

		for ((count=0; count<${var_depth}; count++)); do

			var_fdepth="${var_fdepth}/*"
			var_fstr+=" --include=${var_fdepth}"
		done

		echo "${var_fstr} --exclude=**"
	}


	resetRsStats() {

		local -i count=0

		for ((count=1; count<=16; count+=3)); do
			rs_stats[${count}]=0
			rs_stats[$((${count}+18))]=0
			rs_stats[$((${count}+36))]=0
		done
		for ((count=55; count<=65; count++)); do
			rs_stats[${count}]=0
		done
	}


	# parese the rsync statistics and collects them in rs_stats[@]
	# uses: rs_stats_re_str, rs_stats[@]
	parseRsStats() {

		local var_stats="${1}"
		local count=""

		if ! [[ "${var_stats}" =~ ${rs_stats_re_str} ]]; then

			return 1
		fi

		for count in ${!rs_stats[@]}; do

			[ -n "${BASH_REMATCH[${count}]}" ] && \
				rs_stats[${count}]=$( ${bc_bin} <<< "${rs_stats[${count}]} + \
					${BASH_REMATCH[${count}]//,/}" )
		done

		return 0
	}


	# init rsync slot files for log err and pid infos and create temp files
	# uses: log_lst, err_lst, prc_lst, pid_lst
	setRsyncThreads() {

		local -i var_t=${1}
		local -i var_tlst=${#prc_lst[@]}
		local var_f=""
		local -i count=0

		if [ ${var_tlst} -lt ${var_t} ]; then

			for ((count=${var_tlst}; count<${var_t}; count++)); do

				log_lst[${count}]=$(mkTemp)
				err_lst[${count}]=$(mkTemp)
				prc_lst[${count}]=$(mkTemp)
				pid_lst[${count}]=$(mkTemp)
				echo -n "empty" >${prc_lst[${count}]}
			done

		elif [ ${var_tlst} -gt ${var_t} ]; then

			for ((count=${var_tlst}-1; count>=${var_t}; count--)); do

				for var_f in "${log_lst[${count}]}" "${err_lst[${count}]}" \
				    "${prc_lst[${count}]}" "${pid_lst[${count}]}"; do

					[ -e "${var_f}" ] && Remove "${var_f}" "" 1
				done

				unset log_lst[${count}]
				unset err_lst[${count}]
				unset prc_lst[${count}]
				unset pid_lst[${count}]
			done
		fi
	}


	# tests a rsync slot number
	# uses: pid_lst, prc_lst, log_lst, err_lst, rs_log, rs_elog
	#       opts[rsync_flags,rsync_ign_errors]
	# returns: 0 for a rsync process still running
	#          1 for finished rsync process
	#          2 for an empty slot
	testRsyncThreads() {

		local var_thread_no=${1}
		local -i rc=0
		local var_thread_rc=""

		logCmd 5 "${str_th_chs} #${var_thread_no}"

		while [ -z "${var_thread_rc}" ]; do
			var_thread_rc="$( ${cat_bin} ${prc_lst[${var_thread_no}]} )"
		done

		if [ "${var_thread_rc}" == "empty" ]; then

			# slot is empty
			logCmd 5 "${str_th_e} #${var_thread_no}"
			return 2
		fi

		if [ "${var_thread_rc}" == "running" ]; then

			# rsync in slot is still running
			logCmd 5 "${str_th_spr} #${var_thread_no}"
			return 0
		fi

		# rsync finished
		logCmd 5 "${str_th_spf} #${var_thread_no} rc:${var_thread_rc}"

		# eval rsync return code
		if containsElement "${var_thread_rc}" \
		   ${opts['rsync_ign_errors']//[,;]/ } || \
		   [ ${var_thread_rc} -eq 0 ]; then

			# get stats from log, if avalable
			if [[ "${opts['rsync_flags']}" =~ --stats ]]; then

				logCmd 5 "${str_bu_stats} #${var_thread_no}"
				if ! parseRsStats "$( ${tail_bin} -16 \
				   ${log_lst[${var_thread_no}]} )"; then

					logCmd 1 "${str_bu_stats_e} '$( ${head_bin} -1 ${log_lst[${var_thread_no}]} )'"
				fi
			fi
		else
			[ ${fail_flag} -lt 1 ] && fail_flag=1
		fi

		# push rc and last log in collected log
		${cat_bin} ${log_lst[${var_thread_no}]} >>${rs_log}
		echo "rsync return code: ${var_thread_rc}" >>${rs_log}

		# check error log

		checkErrLog "${err_lst[${var_thread_no}]}"
		rc=${?}

		if [ ${rc} -ne 0 ]; then

			# we store the error log, as there is something in it
			${cat_bin} ${err_lst[${var_thread_no}]} >>${rs_elog}
			echo "rsync return code: ${var_thread_rc}" >>${rs_elog}
		fi

		if [ ${rc} -gt 1 ]; then

			# we want to send an error message
			[ ${fail_flag} -lt 2 ] && fail_flag=2
		fi

		# reset slot
		echo -n "empty" >${prc_lst[${var_thread_no}]}

		return 1
	}


	# options: 0|1 not just wait for all threads, but kill them
	waitRsyncThreads() {

		local -i var_kill_th=${1:-0}
		local -a var_kill_cnt=()
		local -i var_thread_no=0
		local -i var_kill_limit=40
		local var_loop=true
		local var_pid=""

		if [ ${var_kill_th} -eq 1 ]; then

			for ((var_thread_no=0; \
			      var_thread_no<${used_threads}; \
			      var_thread_no++)); do

				var_kill_cnt[${var_thread_no}]=0
			done
		fi

		while ${var_loop}; do

			var_loop=false
			for ((var_thread_no=0; \
			      var_thread_no<${used_threads}; \
			      var_thread_no++)); do

				if testRsyncThreads "${var_thread_no}"; then

					var_loop=true

					if [ ${var_kill_th} -eq 1 ]; then

						var_pid="$( ${cat_bin} ${pid_lst[${var_thread_no}]} )"
						var_pscmd="$( ${ps_bin} -o command --no-headers -p \
							${var_pid} 2>/dev/null)"

						if ! [[ "${var_pscmd}" =~ ^${rsync_bin}\  ]]; then

							logCmd 4 "${str_th_src} #${var_thread_no} (${var_pid})"
							echo -n "-1" >${prc_lst[${var_thread_no}]}
							continue
						fi

						if [ ${var_kill_cnt[${var_thread_no}]} -lt \
						     ${var_kill_limit} ]; then

							kill ${var_pid}
							var_kill_cnt[${var_thread_no}]+=1
						else
							kill -9 ${var_pid}
						fi
					fi
				fi
			done

			sleep 0.25
		done
	}


	# uses: opts[name,last_suffix,log_path], rs_stats, thread_count
	#       rs_log, rs_elog
	handleRsyncLogs() {

		local var_suff=""
		local var_ssuff=".stats"
		local var_esuff=".error"
		local var_lsuff="${opts['last_suffix']}"
		local var_log_path="${opts['log_path']}"
		local var_log_name="${dst_path['name']:-$( getName 2 )}"
		local var_log_base="${var_log_path}/${var_log_name}"
		local var_log="${var_log_base}_${date_str}"
		local var_log_last="${var_log_base}${var_lsuff}"
		local var_log_stats="${var_log}${var_ssuff}"
		local var_elog="${var_log}${var_esuff}"
		local var_elog_last="${var_log_base}${var_esuff}${var_lsuff}"
		local var_time=$( ${bc_bin} <<< "$( ${date_bin} +%s ) - ${bu_start_time}" )
		local var_rate=""

		# move/rename/compress logfile only, if it's not empty
		if [ -s "${rs_log}" ]; then

			var_suff=$( moveTmpLog "${rs_log}" "${var_log}" )

			# create 'last log' link for successfull moved log
			if [ ${?} -eq 0 ] && [ -n "${var_lsuff}" ]; then

				linkCreate "${var_log##*/}${var_suff}" \
				           "${var_log_last}${var_suff}" \
				           "" 1
			fi

			# method '*image': send error message and attach log, if necessary
			if [ "${opts['method']}" != 'rsync' ] && \
			   [ ${fail_flag} -gt 1 ]; then

				logCmd 1 "${str_bu_e_log} '${var_log}${var_suff}'" \
				         "${var_log}${var_suff}"
			fi
		else
			Remove "${rs_log}" "" 1
		fi

		# write stats, if available
		if [ "${opts['method']}" == "rsync" ] && \
		   [[ "${opts['rsync_flags']}" =~ --stats ]]; then

			logCmd 4 "${str_bu_bst} '${var_log_stats}'"

			# calculate total tranfer rate
			#rs_stats[65]=$( ${bc_bin} <<< "${rs_stats[65]} / ${thread_count}" )
			[ ${var_time} -eq 0 ] && var_time=1
			var_rate=$( ${bc_bin} <<< "${rs_stats[57]} / ${var_time}" )

			LANG="${opts['print_lang']}"

			${printf_bin} "Number of files: %'d (reg: %'d, dir: %'d, link: %'d, dev: %'d, special: %'d)\n" \
			${rs_stats[1]} ${rs_stats[4]} ${rs_stats[7]} ${rs_stats[10]} ${rs_stats[13]} ${rs_stats[16]} >"${var_log_stats}"
			if [ -n "${rs_stats[19]}" ]; then
				${printf_bin} "Number of created files: %'d (reg: %'d, dir: %'d, link: %'d, dev: %'d, special: %'d)\n" \
				${rs_stats[19]} ${rs_stats[22]} ${rs_stats[25]} ${rs_stats[28]} ${rs_stats[31]} ${rs_stats[34]} >>"${var_log_stats}"
			fi
			if [ -n "${rs_stats[37]}" ]; then
				${printf_bin} "Number of deleted files: %'d (reg: %'d, dir: %'d, link: %'d, dev: %'d, special: %'d)\n" \
				${rs_stats[37]} ${rs_stats[40]} ${rs_stats[43]} ${rs_stats[46]} ${rs_stats[49]} ${rs_stats[52]}>>"${var_log_stats}"
			fi
			${printf_bin} "Number of regular files transferred: %'d\n" ${rs_stats[55]} >>"${var_log_stats}"
			${printf_bin} "Total file size: %'d bytes\n" ${rs_stats[56]} >>"${var_log_stats}"
			${printf_bin} "Total transferred file size: %'d bytes\n" ${rs_stats[57]} >>"${var_log_stats}"
			${printf_bin} "Literal data: %'d bytes\n" ${rs_stats[58]} >>"${var_log_stats}"
			${printf_bin} "Matched data: %'d bytes\n" ${rs_stats[59]} >>"${var_log_stats}"
			${printf_bin} "File list size: %'d\n" ${rs_stats[60]} >>"${var_log_stats}"
			${printf_bin} "File list generation time: %'.4f seconds\n" ${rs_stats[61]} >>"${var_log_stats}"
			${printf_bin} "File list transfer time: %'.4f seconds\n" ${rs_stats[62]} >>"${var_log_stats}"
			${printf_bin} "Total bytes sent: %'d\n" ${rs_stats[63]} >>"${var_log_stats}"
			${printf_bin} "Total bytes received: %'d\n" ${rs_stats[64]} >>"${var_log_stats}"
			${printf_bin} "Total tranfer rate: %'.2f bytes/sec\n" ${var_rate} >>"${var_log_stats}"
			${printf_bin} "Total time used: %'.4f seconds\n" ${var_time} >>"${var_log_stats}"

			unset LANG

			# create 'last stats' link
			if [ -n "${var_lsuff}" ]; then
				linkCreate "${var_log_stats##*/}" \
				           "${var_log_last}${var_ssuff}" \
				           "" 1
			fi
		fi

		# move/rename/compress error logfile only, if it's not empty
		if [ -s "${rs_elog}" ]; then

			var_suff=$( moveTmpLog "${rs_elog}" "${var_elog}" )

			# create 'last error log' link for successfull moved log
			if [ ${?} -eq 0 ] && [ -n "${var_lsuff}" ]; then

				linkCreate "${var_elog##*/}${var_suff}" \
				           "${var_elog_last}${var_suff}" \
				           "" 1
			fi

			# send error message and attach error log, if necessary
			if [ ${fail_flag} -gt 1 ]; then

				logCmd 1 "${str_bu_e_log} '${var_elog}${var_suff}'" \
				         "${var_elog}${var_suff}"
			fi

		else
			Remove "${rs_elog}" "" 1
		fi

		cleanOldLog "${var_log_path}" "${var_log_name}" ${opts['log_keep']}
	}


	rsWrapper() {

		local var_thread_no=${1}
		local var_rsync="${2}"
		local var_pid=""
		local var_log="${log_lst[${var_thread_no}]}"
		local var_err="${err_lst[${var_thread_no}]}"

		echo "${var_rsync}" >${var_log}
		echo "${var_rsync}" >${var_err}
		eval "${var_rsync} >>${var_log} 2>>${var_err} &"
		var_pid="${!}"
		echo -n "${var_pid}" >${pid_lst[${var_thread_no}]}
		wait ${var_pid}
		echo -n "${?}" >${prc_lst[${var_thread_no}]}
	}


	# uses: src_path[uhost,base,target], dst_path[uhost,base,name,inc,dlnk,target,ussh]
	#       opts[proc_suffix,rsync_flags,log_path,log_keep,last_suffix]
	#       date_str
	rsBackup() {

		local var_dbase="${dst_path['base']}"
		local var_name="${dst_path['name']}"
		local var_inc="${dst_path['inc']}"
		local var_src="${src_path['uhost']}"
		local var_lsrc="${src_path['base']}"
		local var_dst="${dst_path['uhost']}"
		local var_ldst_base="${var_dbase}/${var_name}"
		local var_ldst_final="${var_ldst_base}${var_inc}"
		local var_ldst="${var_ldst_final}"
		local var_subpath=""
		local var_relpath=""
		local var_flags="${opts['rsync_flags']}"
		local var_dlnk="${dst_path['dlnk']}"
		local var_lsuff="${opts['last_suffix']}"
		local var_sflags="${opts['rsync_single_flags']}"
		local var_tflags="${opts['rsync_thread_flags']}"
		local var_depth="${opts['split_depth']}"
		local var_dfilter="$( getDepthFilter ${var_depth} )"
		local -i var_maxthreads=${opts['maxthreads']}
		local var_usshd="${dst_path['ussh']}"
		local var_usshs="${src_path['ussh']}"
		local -i rc=0
		local var_rsync=""
		local var_rsync_b=""
		local var_slog="${src_path['source']:+ (${src_path['source']})${opts['src_relpath']:+/${opts['src_relpath']}}}"
		local var_dlog="${dst_path['source']:+ (${dst_path['source']})${opts['dst_relpath']:+/${opts['dst_relpath']}}}"
		local var_err=""
		local -i var_thread_no=0
		local -i count=0
		local var_tmp=""
		local rm_key="cannot delete non-empty directory: "
		local rm_line=""
		local rm_dir=""
		local var_ftype=""
		local var_loop=true

		bu_start_time=$( ${date_bin} +%s )
		fail_flag=0
		logCmd 4 "${str_bu_maxth} ${var_maxthreads}"
		setRsyncThreads ${var_maxthreads}
		resetRsStats

		# putting the source together
		[ "${var_lsrc: -1}" != "/" ] && var_lsrc+="/"
		var_src+="'${var_lsrc}'"

		# putting the destination together
		# use a processing suffix, if we're creating an incremental backup
		[ -n "${var_dlnk}" ] && var_ldst+="${opts['proc_suffix']}"
		[ "${var_ldst: -1}" != "/" ] && var_ldst+="/"
		var_dst+="'${var_ldst}'"

		# first or single rsync process

		# building rsync string
		var_rsync="${rsync_bin} ${var_flags}"
		[ -n "${opts['dry_run']}" ] && var_rsync+=" --dry-run"
		# append --protect-args if some path contains spaces
		[[ "${var_lsrc}${var_ldst}" == *" "* ]] && var_rsync+=" --protect-args"
		[ -n "${var_sflags}" ] && var_rsync+=" ${var_sflags}"
		[ ${var_maxthreads} -gt 1 ] && [ -n "${var_dfilter}" ] && \
			var_rsync+=" ${var_dfilter}"
		[ -n "${var_dlnk}" ] && var_rsync+=" --link-dest='${var_dlnk}'"
		var_rsync+=" ${var_src} ${var_dst}"
		logCmd 4 "${var_rsync}"

		used_threads=1
		logCmd 3 "${str_bu_st} ${var_src}${var_slog} -> ${var_dst}${var_dlog}"

		echo -n "running" >${prc_lst[${var_thread_no}]}
		rsWrapper ${var_thread_no} "${var_rsync}"
		thread_count=1
		testRsyncThreads "${var_thread_no}"

		# only enter the thread loops, if we have more than one thread
		if [ ${var_maxthreads} -gt 1 ] && \
		   [ ${var_depth} -gt 0 ]; then

			# remove deleted directories in scan depth level
			if [[ "${var_flags}" =~ --del ]]; then

				logCmd 3 "${str_bu_del}"

				while read rm_line; do

					if ! [[ "${rm_line}" =~ ${rm_key}(.*) ]]; then
						logCmd 1 "${str_bu_delee} '${rm_line}'"
						continue
					fi
					rm_dir="${BASH_REMATCH[1]}"

					# file counts for stats
					if [[ "${var_flags}" =~ --stats ]]; then
						count=40
						logCmd 4 "${str_bu_del_stats} '${var_ldst}${rm_dir}'"
						for var_ftype in "-type f" "-type d" "-type l" \
							"\( -type b -o -type c \)" \
							"\( -type s -o -type p \)"; do
							
							var_tmp=$( ${var_usshd:-eval} \
								"${find_bin} '${var_ldst}${rm_dir}' ${var_ftype} 2>/dev/null" | \
								${wc_bin} -l )
							rs_stats[${count}]=$( ${bc_bin} <<< "${rs_stats[${count}]} + ${var_tmp}" )
							count+=3
							rs_stats[37]=$( ${bc_bin} <<< "${rs_stats[37]} + ${var_tmp}" )
						done
					fi

					Remove "${var_ldst}${rm_dir}" "${var_usshd}" 0 1

				done < <( ${grep_bin} "${rm_key}" ${log_lst[${var_thread_no}]} )
			fi

			logCmd 3 "${str_bu_startth} ${var_maxthreads}"
			used_threads=${var_maxthreads}

			# building the base rsync string
			var_rsync_b="${rsync_bin} ${var_flags}"
			[ -n "${opts['dry_run']}" ] && var_rsync_b+=" --dry-run"
			[ -n "${var_tflags}" ] && var_rsync_b+=" ${var_tflags}"

			# process all the sub paths in scan depth
			while read var_subpath; do

				# loop for ever, until we have an empty slot
				while true; do

					# loop though the rsync thread slots
					for ((var_thread_no=0; var_thread_no<${used_threads}; var_thread_no++)); do

						# find an empty slot or continue with the next one
						testRsyncThreads "${var_thread_no}" && continue

						# quit processing sub paths, if the destination volume is full
						if [ ${nospace_flag} -ne 0 ]; then
							logCmd 1 "${str_p_dst_ns} ${dst_path['uhost']}'${dst_path['target']:-/}'${var_dlog}"
							break 3
						fi

						[ "${var_subpath: -1}" != "/" ] && var_subpath+="/"
						var_relpath="${var_subpath:${#var_lsrc}}"

						# TODO: this would be the right point to wait, if the current
						#       system load is to high, and delay the next rsync process

						# decrement created dir, if target doesn't exist on non incremental
						# backups or incremental backups where it doesn't exist in link
						# destination
						if ! ${var_usshd:-eval} "${test_bin} -d \"${var_ldst}${var_relpath}\" && ( \
						   ${test_bin} -z \"${var_dlnk}\" || \
						   ${test_bin} -d \"${var_dlnk}${var_relpath}\" )"; then

							rs_stats[19]=$( ${bc_bin} <<< "${rs_stats[19]} - 1" )
							rs_stats[25]=$( ${bc_bin} <<< "${rs_stats[25]} - 1" )
						fi

						# decrement dir in file count for each thread, to
						# avoid double counting of those
						rs_stats[1]=$( ${bc_bin} <<< "${rs_stats[1]} - 1" )
						rs_stats[7]=$( ${bc_bin} <<< "${rs_stats[7]} - 1" )

						# finish the rsync command string
						var_rsync="${var_rsync_b}"
						# only add a link destination if set and existent
						if [ -n "${var_dlnk}" ] && \
						   ${var_usshd:-eval} "${test_bin} -d '${var_dlnk}${var_relpath}'"; then

							 var_rsync+=" --link-dest='${var_dlnk}${var_relpath}'"
						fi

						# append --protect-args if some path contains spaces
						[[ "${var_subpath}${var_dlnk}${var_dst}${var_relpath}" == *" "* ]] && \
							var_rsync+=" --protect-args"
						var_rsync+=" ${src_path['uhost']}'${var_subpath}' ${var_dst}'${var_relpath}'"

						echo -n "running" >${prc_lst[${var_thread_no}]}
						rsWrapper ${var_thread_no} "${var_rsync}" &
						logCmd 5 "${str_th_sps} #${var_thread_no} '${var_subpath}'"
						thread_count+=1
						continue 3
					done
					logCmd 5 "${str_th_wfs} '${var_subpath}'"
					sleep 0.25

					# TODO: do we really loop for ever, or do we declare existing rsync
					#       jobs for dead at some point?
				done

			done < <( ${var_usshs:-eval} "${find_bin} '${var_lsrc}' \
				-mindepth ${var_depth} -maxdepth ${var_depth} -type d 2>/dev/null" )
	
			# wait until all rsync has jobs finished
			logCmd 4 "${str_th_wff}"
			waitRsyncThreads
		fi

		# care about the log stuff
		handleRsyncLogs

		# rename backup from processing suffix to final dst
		# in case of an incremental backup and
		# create 'last backup' link if a 'last' suffix is set
		if [ ${fail_flag} -lt 2 ]; then

			logCmd 3 "${str_bu_s} ${var_src}${var_slog}"

			if [ -n "${var_inc}" ]; then

				if [ -n "${var_dlnk}" ]; then
					pathRename "${var_ldst%/}" "${var_ldst_final}" "${var_usshd}"
				fi

				if [ -n "${var_lsuff}" ]; then

					linkCreate "${var_ldst_final##*/}" \
						   "${var_ldst_base}${var_lsuff:$( \
					             [ -z "${var_name}" ] && \
					             [[ "${var_lsuff:0:1}" =~ ^[~._-]$ ]] && \
					             echo 1 || echo 0 )}" \
						   "${var_usshd}"
				fi
			fi

		else
			logCmd 1 "${str_bu_e} ${var_src}${var_slog}"
		fi

		return 0
	}


	# FIXME: 2>>${rs_log} or 2>>${tmp_file1} eats all console output (log)
	ncBackup() {

		local var_dbase="${dst_path['base']}"
		local var_name="${dst_path['name']}"
		local var_inc="${dst_path['inc']}"
		local var_dmnt="${dst_path['target']}"
		local var_ldst_base="${var_dbase}/${var_name}"
		local var_ldst_final="${var_ldst_base}${var_inc}"
		local var_ldst="${var_ldst_final}"
		local var_smapper="${src_path['mapper']}"
		local var_dmapper="${dst_path['mapper']}"
		local var_img_name="${var_smapper##*/}"
		local -a var_splst=( ${src_path['plst']} )
		local -a var_dplst=()
		local var_spart=""
		local var_dpart=""
		local var_pinfo=""
		local var_pdelim=""
		local -i var_partnum=0
		local var_ncflags="${opts['nclone_flags']}"
		local var_pcflags="${opts['pclone_flags']}"
		local var_lz4flags="${opts['lz4_flags']}"
		local var_lsuff="${opts['last_suffix']}"
		local var_nclone_bin="${ntfsclone_bin}${var_ncflags:+ ${var_ncflags}}"
		local var_pclone_bin="${partclone_bin}${var_pcflags:+ ${var_pcflags}}"
		local var_lz4_bin="${lz4_bin} ${var_lz4flags} -qq -"
		local var_lz4d_bin="${lz4_bin} ${var_lz4flags} -d -qq -"
		local var_buffer_bin="${buffer_bin} -m 8m"
		local var_usshd="${dst_path['ussh']}"
		local var_usshd_si="${var_usshd/-n /}"
		local var_usshs="${src_path['ussh']}"
		local -i rc=0
		local var_dd_dl=""
		local -i var_dls=0
		local var_clone=""
		local var_ctype=""
		# local var_slog="${src_path['source']:+ (${src_path['source']})${opts['src_relpath']:+/${opts['src_relpath']}}}"
		local var_dlog="${dst_path['source']:+ (${dst_path['source']})${opts['dst_relpath']:+/${opts['dst_relpath']}}}"
		local var_dryrun="${opts['dry_run']}"
		local var_ncpcln='[[:space:]]*[.[:digit:]]{3,6} percent completed'
		local var_pcpcln='((Elapsed|Remaining): [[:digit:]:]{8}|Complete: +[[:digit:].]{4,6}%)'
		local var_partx_flags="--noheadings --pairs --bytes --nr 1 --output START,SECTORS,SIZE"
		local tmp_data=""

		local -i START=0
		local -i SECTORS=0
		local -i SIZE=0

		# test for binaries locally or remote
		checkBinSave "nclone" "${nclone_bin_lst[*]}" "${var_usshs}" || return 1

		bu_start_time=$( ${date_bin} +%s )
		fail_flag=0

		# empty temp log files
		echo -n "" >${rs_log}

		# putting the destination together, if we write an image
		if [ ${opts['disk2disk']} -eq 0 ]; then
			# use a processing suffix, if we're creating an incremental backup
			[ -n "${var_inc}" ] && var_ldst+="${opts['proc_suffix']}"
			[ "${var_ldst: -1}" != "/" ] && var_ldst+="/"
			if [ -z "${var_dryrun}" ]; then
				pathCreate "${var_ldst}" "${var_usshd}"|| return 1
			fi
		fi

		# override disk label size calculation
		if [ -n "${opts['disk_label_size']}" ]; then

			var_dls=${opts['disk_label_size']}
		else
			# retrieve and calculate disk label size
			tmp_data="$( ${var_usshs:-eval} "${partx_bin} ${var_partx_flags} \
				${var_smapper} 2>/dev/null" )"

			if [ ${?} -ne 0 ]; then
				logCmd 1 "${str_nc_rdlif} ${var_smapper}"
				return 1
			fi
			eval "${tmp_data}"
			var_dls=$(( ${SIZE} / ${SECTORS} * ${START} ))
		fi

		# building dd strings
		var_dd_dl="${var_usshs:-eval} \"${dd_bin} if=${var_smapper} bs=${var_dls} "
		var_dd_dl+="count=1\" 2>>${rs_log} | ${var_usshd_si:-eval} "

		if [ ${opts['disk2disk']} -eq 0 ]; then
			var_dd_dl+="\"${cat_bin} > '${var_ldst}${var_img_name}_disklabel'\" 2>>${rs_log}"
			logCmd 3 "${str_bu_st} '${src_path['uhost']}${src_path['dpath']}' -> '${var_ldst}${var_dlog}'"
		else
			var_dd_dl+="\"${dd_bin} of=${var_dmapper} bs=${var_dls}\" 2>>${rs_log}"
			logCmd 3 "${str_bu_st} '${src_path['uhost']}${src_path['dpath']}' -> '${dst_path['uhost']}${dst_path['dpath']}'"
		fi

		# save disk label or clone sub part table
		if [ -n "${var_dryrun}" ]; then

			logCmd 3 "${var_dd_dl}"
		else
			logCmd 4 "${var_dd_dl}"
			eval "${var_dd_dl}"
			rc=${?}

			logCmd 4 "${str_nc_ddrc} ${rc}"
			echo "return code: ${rc}" >>${rs_log}

			if [ ${rc} -ne 0 ]; then
				logCmd 1 "${str_nc_ddf} ${var_smapper}"
				fail_flag=2
			fi
		fi

		if [ ${opts['disk2disk']} -eq 1 ]; then

			if [ ${fail_flag} -gt 1 ]; then
				# stop in case of a fail_flag during a disk2disk operation
				handleRsyncLogs
				return 1
			fi

			# activate partition table on device
			# and get partition list
			if [ -n "${opts['dry_run']}" ]; then

				logCmd 3 "${str_nc_drapt} ${var_dmapper}"
			else

				if ! dst_path['plst']="$( ptableSave 'dst_path' )"; then
					handleRsyncLogs	
					return 1
				fi
				var_dplst=( ${dst_path['plst']} )
			fi
		fi

		# imaging partitions
		for var_spart in "${var_splst[@]}"; do
		
			var_img_name="${var_spart##*/}"
			# clear tmp file
			echo -n "" >${tmp_file1}

			# determin filesystem type of sub partition or raw for "data" partitions
			var_pinfo=$( ${var_usshs:-eval} "${file_bin} -sL '${var_spart}'" )

			if [[ "${var_pinfo}" =~ (:\ data$|\ raw\ data) ]]; then
				var_ctype="dd"
			elif [[ "${var_pinfo}" =~ NTFS ]]; then
				var_ctype="nc"
			elif [[ "${var_pinfo}" =~ FAT\ \((16|32)\ bit\) ]]; then
				checkBinSave "pclone" "${pclone_bin_lst[*]}" "${var_usshs}" || return 1
				var_ctype="pc"
			fi

			# building clone string
			case "${var_ctype}" in

				"nc")
					var_clone="${var_usshs:-eval} \"${var_nclone_bin} "
					var_clone+="--save-image --output - '${var_spart}' | "
				;;

				"dd")
					var_clone="${var_usshs:-eval} \"${dd_bin} bs=1M if='${var_spart}' | "
				;;

				"pc")
					var_clone="${var_usshs:-eval} \"${var_pclone_bin} "
					var_clone+="--clone --quiet --source '${var_spart}' --output - | "
				;;
			esac

			var_clone+="${var_buffer_bin} | ${var_lz4_bin} | "
			var_clone+="${var_buffer_bin}\" 2>>${tmp_file1} | ${var_usshd_si:-eval} "

			if [ ${opts['disk2disk']} -eq 0 ]; then

				# quit processing partitions, if the destination volume is full
				if [ ${nospace_flag} -ne 0 ]; then
					logCmd 1 "${str_p_dst_ns} '${dst_path['uhost']}${dst_path['target']:-/}${var_dlog}'"
					break
				fi

				# complete clone string
				var_clone+="\"${var_buffer_bin} -p 75 > "
				var_clone+="'${var_ldst}${var_img_name}.${var_ctype}.lz4'\" 2>>${tmp_file1}"
			else
				# extract partition number
				[[ "${var_spart: -2}" =~ ^([[:digit:]]+)$ ]] || \
					[[ "${var_spart: -1}" =~ ^([[:digit:]]+)$ ]]

				if [ ${?} -ne 0 ]; then
					logCmd 1 "${str_nc_udpn} ${var_spart}"
					continue
				fi

				var_partnum=${BASH_REMATCH[1]}

				# find partition number in dst
				if [ -n "${opts['dry_run']}" ]; then

					[[ ${var_dmapper: -1} =~ ^[[:digit:]]$ ]] && \
						var_pdelim="p" || var_pdelim=""
					var_dpart="${var_dmapper}${var_pdelim}${var_partnum}"
					logCmd 3 "${str_nc_drasp} ${var_dpart}"
				else
					for var_dpart in "${var_dplst[@]}"; do
						[ ${var_dpart: -${#var_partnum}} -eq ${var_partnum} ] && \
							break || var_dpart=""
					done
				fi

				if [ -z "${var_dpart}" ]; then
					logCmd 1 "${str_nc_uddp} ${var_spart}"
					continue
				fi

				logCmd 4 "${str_nc_bdp} ${var_dpart}"

				# complete clone string
				var_clone+="\"${var_buffer_bin} | ${var_lz4d_bin} | ${var_buffer_bin} | "

				case "${var_ctype}" in

					"nc")
						var_clone+="${var_nclone_bin} --restore-image "
						var_clone+="--overwrite '${var_dpart}' -\" >>${tmp_file1} 2>&1"
					;;

					"dd")
						var_clone+="${dd_bin} bs=1M of=${var_dpart}\" 2>>${tmp_file1}"
					;;

					"pc")
						var_clone+="${var_pclone_bin} --restore --quiet "
						var_clone+="--output '${var_dpart}' --source -\" >>${tmp_file1} 2>&1"
					;;
				esac
			fi

			# execute actual cloning
			if [ -n "${var_dryrun}" ]; then

				logCmd 3 "${var_clone}"
			else
				logCmd 4 "${var_clone}"
				eval "${var_clone}"
				rc=${?}

				case "${var_ctype}" in

					"nc")
						logCmd 4 "${str_nc_rc} ${rc}"
						echo "${str_nc_rc} ${rc}" >>${tmp_file1}
					;;

					"dd")
						logCmd 4 "${str_nc_ddrc} ${rc}"
						echo "${str_nc_ddrc} ${rc}" >>${tmp_file1}
					;;

					"pc")
						logCmd 4 "${str_nc_pcrc} ${rc}"
						echo "${str_nc_pcrc} ${rc}" >>${tmp_file1}
					;;
				esac

				if [ ${rc} -ne 0 ]; then 
					logCmd 1 "${str_nc_e} '${var_spart}'"
					fail_flag=2
				fi

				# TODO: move to checkErrLog after merge with master
				# test for "No space left on device"
				if ${grep_bin} -q 'No space left on device' "${tmp_file1}"; then

					if ! ${var_ussh:-eval} "${touch_bin} '${var_dmnt}/nospace_lock'"; then
						logCmd 1 "${str_log_err_cens}: '${var_dmnt}'"
					fi
					nospace_flag=1
					fail_flag=2
					logCmd 1 "${str_log_err_ns}"
				fi

				# clean log from percentage count
				${grep_bin} -v -E "${var_ncpcln}" ${tmp_file1} | ${grep_bin} -v -E "${var_pcpcln}" >> ${rs_log}
			fi
		done

		# care about the log stuff
		handleRsyncLogs

		# rename backup from processing suffix to final dst
		# in case of an incremental backup and
		# create 'last backup' link if a 'last' suffix is set
		if [ ${fail_flag} -lt 2 ]; then

			logCmd 3 "${str_nc_s} '${src_path['uhost']}${src_path['dpath']}'"

			if [ -n "${var_inc}" ]; then

				pathRename "${var_ldst%/}" "${var_ldst_final}" "${var_usshd}"

				if [ -n "${var_lsuff}" ]; then

					linkCreate "${var_ldst_final##*/}" \
						   "${var_ldst_base}${var_lsuff:$( \
					             [ -z "${var_name}" ] && \
					             [[ "${var_lsuff:0:1}" =~ ^[~._-]$ ]] && \
					             echo 1 || echo 0 )}" \
						   "${var_usshd}"
				fi
			fi

		else
			logCmd 1 "${str_nc_e} '${src_path['uhost']}${src_path['dpath']}'"
		fi

		return 0
	}


	parseOptions() {

		local fn=""

		# check all bool values
		for fn in "${bool_lst[@]}"; do

			if [ -n "${opts[${fn}]}" ] && \
			   [[ ! "${opts[${fn}]}" =~ ^[01]$ ]]; then

				logCmd 1 "${str_opt_oe} '${fn}=0|1'"
				return 1
			fi
		done

		# check bu_set
		if [[ ! "${opts['bu_set']}" =~ ^[0-9]$ ]]; then

			logCmd 1 "${str_opt_bu_set_e}"
			return 1
		fi

		# check for unknown backup method
		if ! containsElement "${opts['method']}" "${method_lst[@]}"; then

			logCmd 1 "${str_opt_meth_u} ${opts['method']}"
			return 1
		fi

		# check logs keep time
		if [[ ! "${opts['log_keep']}" =~ ^[0-9]+$ ]]; then

			logCmd 1 "${str_opt_log_keep_e}"
			return 1
		fi

		# check snap_size
		if [ -n "${opts['snap_size']}" ] && \
		   [[ ! "${opts['snap_size']}" =~ ^[0-9]+[kKmMgGtTpPeE]?$ ]]; then

			logCmd 1 "${str_opt_snap_size_e}"
			return 1
		fi

		# check source
		if [ -n "${opts['source']}" ] && \
		   [[ "${opts['source']}" =~ ^.+\*/?$ ]] && \
		   [[ "${opts['source']}" =~ [,\;] ]]; then

			logCmd 1 "${str_opt_src_e}"
			return 1
		fi

		# check / create work paths
		for fn in "log_path" "lib_path" "confd"; do

			opts[${fn}]="${opts[${fn}]/%\/}"
			pathCreate "${opts[${fn}]}" || return 1
		done

		# check mount paths
		for fn in "src_mnt_base" "dst_mnt_base"; do

			opts[${fn}]="${opts[${fn}]/%\/}"

			if [ -z "${opts[${fn}]}" ]; then
				logCmd 1 "${str_opt_pe} '${fn}'"
				return 1
			fi
		done

		# check dst_fs_type for known file systems
		if [ -n "${opts['dst_fs_type']}" ] && \
		   ! containsElement "${opts['dst_fs_type']}" "${fs_types[@]}"; then

			logCmd 1 "${str_opt_ufst} '${opts['dst_fs_type']}'"
			return 1
		fi

		return 0
	}


	# setup the main script configuration
	# options: the command line aguments
	# provides: rsync_ign_msg_lst (opts, cl_opts, gf_opts)
	# uses: opts, cl_opts, gf_opts, df_opts, e_opts_lst, g_opts_lst
	#       (n_opts_lst)
	startupConfig() {

		local var_md5=""
		local var_lib_path=""
		local fn=""

		# test for basic binaries
		checkBinSave "base" "${base_bin_lst[*]}" || exit 1
		var_md5="$( checkBinSave "md5" "${md5_bin_lst[*]}" "" 1 )" || exit 1

		# evaluate command line arguments
		evalArgs "${@}"
		setOptions "${e_opts_lst[@]}"
		loadLang "${opts['bu_lang']}" || return 1

		# log command line arguments
		logCmd 5 "${str_opt_cmd}"
		for fn in "${!cl_opts[@]}"; do
			if containsElement "${fn}" "${pwd_lst[@]}"; then
				logCmd 5 "cl_opts['${fn}']='********'"
			else
				logCmd 5 "cl_opts['${fn}']='${cl_opts[${fn}]}'"
			fi
		done

		# get options from global configuration file
		logCmd 3 "${str_opt_rglob} ${opts['config']}"
		readConfFile "${opts['config']}" "gf_opts" \
		  "${g_opts_lst[@]}" || return 1
		setOptions "${g_opts_lst[@]}"

		# append backup specific path to lib_path
		if [ "${var_md5}" == "md5sum" ]; then

			var_lib_path=$( echo -n "${opts['config']}" | ${md5sum_bin} - | ${cut_bin} -f 1 -d ' ' )
		else
			var_lib_path=$( ${md5_bin} -s "${opts['config']}" | ${cut_bin} -f 2 -d '=' )
		fi
		opts['lib_path']="${opts['lib_path']/%\/}/${var_lib_path// }"
		logCmd 4 "${str_opt_ulp} '${opts['lib_path']}'"

		# some basic option tests
		parseOptions || return 1
		loadLang "${opts['bu_lang']}" || return 1

		# log options from global configuration file
		logCmd 5 "${str_opt_gcf}"
		for fn in "${!gf_opts[@]}"; do
			if containsElement "${fn}" "${pwd_lst[@]}"; then
				logCmd 5 "gf_opts['${fn}']='********'"
			else
				logCmd 5 "gf_opts['${fn}']='${gf_opts[${fn}]}'"
			fi
		done

		# log all options
		logCmd 5 "${str_opt_col}"
		for fn in "${g_opts_lst[@]}" "${h_opts_lst[@]}"; do
			if containsElement "${fn}" "${pwd_lst[@]}"; then
				logCmd 5 "opts['${fn}']='********'"
			else
				logCmd 5 "opts['${fn}']='${opts[${fn}]}'"
			fi
		done

		# load the ignore error file
		if [ -f "${opts['rsync_ign_msg']}" ]; then
			logCmd 4 "${str_log_ign_load} '${opts['rsync_ign_msg']}'"
			while read fn; do
				rsync_ign_msg_lst+=( "${fn}" )
			done < "${opts['rsync_ign_msg']}"

			logCmd 5 "${str_opt_ign}"
			for fn in "${!rsync_ign_msg_lst[@]}"; do
				logCmd 5 "[${fn}]: '${rsync_ign_msg_lst[${fn}]}'"
			done
		fi

		return 0
	}


	# setup the base configuration for the current backup
	# and query source and destination
	# options: configuration file
	# provides: src_path, dst_path, (cb_opts, opts)
	# uses: src_path, dst_path, b_opts_lst  
	backupConfig() {

		local var_cf="${1}"
		local tmp_data=""

		# load configuration of the current backup
		logCmd 3 "${str_opt_rbu} '${var_cf}'"
		cb_opts=()
		readConfFile "${var_cf}" "cb_opts" "${b_opts_lst[@]}" || return 1
		setOptions "${b_opts_lst[@]}"
		parseOptions || return 1

		# log current backup options
		logCmd 5 "${str_opt_bcf}"
		for fn in "${!cb_opts[@]}"; do
			if containsElement "${fn}" "${pwd_lst[@]}"; then
				logCmd 5 "cb_opts['${fn}']='********'"
			else
				logCmd 5 "cb_opts['${fn}']='${cb_opts[${fn}]}'"
			fi
		done

		# query full backup source path
		logCmd 3 "${str_bu_src_q} '${opts['source']}'"
		tmp_data=$( queryPath "${opts['source']}" )
		if [ ${?} -ne 0 ]; then
			logCmd 1 "${str_bu_src_path_qe} '${opts['source']}'"
			return 1
		fi
		eval "src_path=( ${tmp_data} )"

		# log source path
		logCmd 5 "${srt_opt_src_a}"
		for fn in "${!src_path[@]}"; do
			logCmd 5 "src_path['${fn}']='${src_path[${fn}]}'"
		done

		# query full backup destination path
		logCmd 3 "${str_bu_dst_q} '${opts['destination']}'"
		tmp_data=$( queryPath "${opts['destination']}" )
		if [ ${?} -ne 0 ]; then
			logCmd 1 "${str_bu_dst_path_qe} '${opts['destination']}'"
			return 1
		fi
		eval "dst_path=( ${tmp_data} )"

		# log destination path
		logCmd 5 "${srt_opt_dst_a}"
		for fn in "${!dst_path[@]}"; do
			logCmd 5 "dst_path['${fn}']='${dst_path[${fn}]}'"
		done

		# check that not both source and destination are remote - at least for now
		if [ -n "${src_path['ussh']}" ] && [ -n "${dst_path['ussh']}" ]; then
			logCmd 1 "${str_bu_all_rem} ${src_path['full']}, ${dst_path['full']}"
			return 1
		fi

		# we only allow vg as destination if source is either vg or lv
		if [ "${dst_path['type']}" == "vg" ] && \
		   [[ ! "${src_path['type']}" =~ ^(vg|lv)$ ]]; then
			logCmd 1 "${str_bu_dst_na} ${src_path['type']}"
			return 1
		fi

		return 0
	}


	# check that the same rsync-backup is not in progress and create lockfile
	checkRunning() {

		local lockfile="${opts['lib_path']}/lockfile"

		if [ -e "${lockfile}" ]; then

			if [[ "$( ${ps_bin} -o command --no-headers -p \
			      $( ${cat_bin} "${lockfile}" ) )" =~ rsync-backup ]]; then

				logCmd 1 "${str_bu_sr}"
				return 1
			else
				logCmd 2 "${str_bu_rl}"
			fi
		fi

		echo $$ > "${lockfile}"
	}


	usbStart() {

		if ! usbResume "${opts['usb_dev']}" "${opts['usb_podev']}" \
		   "${opts['usb_posoc']}" "${opts['usb_uhost']}"; then

			logCmd 1 "usb device start failed"
			return 1
		fi
	}


	usbStop() {

		if ! usbSuspend "${opts['usb_dev']}" "${opts['usb_podev']}" \
		   "${opts['usb_posoc']}" "${opts['usb_uhost']}"; then

			logCmd 1 "usb device stop failed"
			return 1
		fi 
	}



### script main entry point ###

# set some early presets to get log output
# and set hidden opts
setOptions "${e_opts_lst[@]}"
setOptions "${h_opts_lst[@]}"
loadLang "${opts['bu_lang']}" || exit 251

# to get early debug output
# opts['debug']=4

# create some temp files
for tf in "${tmpfiles[@]}"; do
	eval "${tf}=\$( mkTemp )"
done

# catch ctrl+c
trap sigHandler 2 15
# load global configuration and parse parameters
startupConfig "${@}" || myExit 251

# check that the same rsync-backup is not in progress and create lockfile
checkRunning || myExit 250

# power up usb devices
if [ ${opts['usb_global']} -eq 1 ]; then
	usbStart || myExit 249
fi

# get all backup configuration files
logCmd 4 "${str_bu_scan} '${opts['confd']}'"
find_expr="${find_bin} '${opts['confd']}' -maxdepth 1 -regextype posix-egrep "
find_expr+="-regex '^${opts['confd']}/[^.][^/]+\.(conf|cfg)$'"
IFS_save="${IFS}"
IFS=$'\x0a'
backups=( $( IFS="${IFS_save}" ; eval "${find_expr}" 2>/dev/null | sort ) )
IFS="${IFS_save}"

# log backup conf files
for fn in "${!backups[@]}"; do
	logCmd 5 "${str_bu_f} [${fn}]: '${backups[${fn}]}'"
done

# process backup configurations
for cf in "${backups[@]}"; do

	# setup backup config and query src, dst
	backupConfig "${cf}" || continue

	# power up usb devices
	if [ ${opts['usb_global']} -eq 0 ]; then
		usbStart || continue
	fi

	# we always enter the loop, even if the source isn't lvm
	for lv in ${src_path['lvs']}; do

		# stop xen domU if xen_dom option is set
		xenStop
		[ ${?} -lt 2 ] || continue

		# prepare (and mount) source path
		if ! prepareSrc "${lv}"; then xenStart true; continue; fi

		# start xen domU, if we use snapshot
		xenStart true

		# prepare (and mount) destination path"
		if ! prepareDst; then Cleanup && continue; fi

		# TODO: do we use the naming rule suffix/prefixes here?
		# get destination name
		dst_path['name']=$( getName ${opts['name_rule']} )
		logCmd 4 "${str_bu_name} '${dst_path['name']}'"

		# check for nospace_lock (only if we write to an actual filesystem)
		if ! containsElement "${opts['method']}" "${clone_methods[@]}" || \
		   [ ${opts['disk2disk']} -eq 0 ]; then

			if dst_path['target']="$( testLock )"; then
				nospace_flag=0
			else
				Cleanup && continue
			fi
		fi

		# test for incremental backup and remove expired backups
		if ! testIncremental; then
			Cleanup && continue
		fi

		case ${opts['method']} in

			rsync)     rsBackup;;
			partimage) piBackup;;
			partclone) pcBackup;;
			ntfsclone) ncBackup;;
		esac

		Cleanup
		[[ "${src_path['type']}" =~ ^vg$ ]] && logCmd 3 "${str_bu_ce} '${src_lvs['volume']}'"
	done
	logCmd 3 "${str_bu_be} '${cf}'"

	# power down usb devices
	if [ ${opts['usb_global']} -eq 0 ]; then
		usbStop || continue
	fi
done

# power down usb devices
if [ ${opts['usb_global']} -eq 1 ]; then
	usbStop || myExit 249
fi

myExit 0

######################################################################################

# TODO:
#   add backup name and log name rule
#   support other fs than ext, specially for resize
#   redo documentation
#   more var-checks

# TODO: ignore "usb_dev" "usb_podev" "usb_posoc" "usb_uhost" "usb_pm" in
#       backup configuration if "usb_global" is set

# do we have to keep a list of snapshots similar to mounts?
# how do we handle already existing snapshots?
# use existing snapshots rule ?

# put --protect-args into default rsync flags?
# TODO: mktemp auf mac testen

# TODO: parameter test loop

# TODO: unable to diff between no network connection and power outlet is already off
# TODO: usbSuspend: better test for lvm pv's in devices and it's partitions
# TODO: do not suspend and power off usb device if umount failed !

# TODO: test 'echo, kill, eval' bin

######################################################################################

