#!/bin/sh -u
#
# -u: nounset (enable: set -u | disable: set +u)
# -e: errexit
# -r: restricted
#
#
# VARIABLE CONVENTIONS
#
# 1. Function variables
# ----------------------
# * Lowercase
# * Prefixed with _
# Example: _tmp, _dir, _len
#
# 2. Config file variables
# ------------------------
# * Uppercase
# * No prefix
# Example: MYSQL_OPTS
#
# 3. Default values for config file variables
# -------------------------------------------
# * Uppercase
# * Prefixed with _DEFAULT_
# * Same name as config file variables
# Example: _DEFAULT_CONFIG_FILE
#
# 4. Global program variables
# -------------------------------------------
# TODO: Also think about other schema, so I don't mix
#       them up with vars from the config.
# * Uppercase
# * Prefixed with identifier, such as: MDS_[SECTION]_, DB_[SECTION]_
# Example: MDS_INFO_NAME, DB_CNT_FAILED
#
# 5. Variables within loops / Temporary variables
# -------------------------------------------
# * Uppercase
# * Prefixed with _TMP
# Example: _TMP_RESULT <-- TODO: Fix this


#
# Normalize
# Don't screw with numbers, sorting, date etc
#
LANG=C
LC_ALL=C



####################################################################################################
####################################################################################################
##
## GLOBAL VARIABLES
##
####################################################################################################
####################################################################################################


################################################################################
#
# VERSION / CREDITS
#
################################################################################

#
# Some of this data is also written to the nagios logfile
# in order to be able to check against latest/desired mysqldump-secure version.
#
# TODO: Prefix with MDS_
INFO_NAME="mysqldump-secure"
INFO_AUTHOR="Patrick Plocke <patrick@plocke.de>"
INFO_GPGKEY="0x28BF179F"
INFO_LICENSE="MIT"
INFO_URL="http://mysqldump-secure.org"
INFO_REPO="https://github.com/cytopia/mysqldump-secure"
INFO_DATE="2016-08-18"
INFO_VERSION="0.16.3"



################################################################################
#
# AUTO-GENERATED
#
################################################################################

# The following are auto-generated variables by the installer.
# to determine where the script and its config files are installed.
_PREFIX_ETC="/usr/local"
_PREFIX_LOG="/usr/local"
_PREFIX_VAR="/usr/local"




################################################################################
#
# EXIT CODES
#
################################################################################

# Everything went fine.
# No errors and no warnings.
EXIT_OK=0

# The database dump was successful, but some other
# warnings occured.
# E.g.: tmpreaper/tmpwatch could not delete files, etc
EXIT_WARNING=1

# The database dump was successful, but some other
# errors occured.
# E.g.: Directories could not be created, etc
EXIT_ERROR=2

# The database dump had errors.
# E.g.: File already existed, wrong mysqldump options, wrong compression options, etc
EXIT_FAIL=3

# The program terminated before it could do backups
# E.g.: No database connection, wrong config settings, etc
EXIT_ABORT=4


# Internal program error.
# This should never happen, but when it does
# the code is wrong
EXIT_INT_ERR=5




################################################################################
#
# WARNING, ERROR, FAIL COUNTER
#
################################################################################

#
# How many warnings occured
#
MDS_WARNING_COUNT=0

#
# How many errors occured
#
MDS_ERROR_COUNT=0


#
# How many failed database dumps occured
#
MDS_FAIL_COUNT=0




################################################################################
#
# DEFAULT VARIABLES
#
################################################################################

#
# Do not djust the following default variables!
# They can be overwritten inside mysqldump-secure.conf
# or via command line arguments
#

# Default Configuration Path/Name
_DEFAULT_CONFIG_FILE="${_PREFIX_ETC}/etc/mysqldump-secure.conf"
_DEFAULT_CONFIG_CHMOD="0400"

# Default output Variables
_DEFAULT_DUMP_DIR="${_PREFIX_VAR}/var/mysqldump-secure"
_DEFAULT_DUMP_DIR_CHMOD="0700"

_DEFAULT_DUMP_FILE_PRE="$(date '+%Y-%m-%d')_$(date '+%H-%M')__"
_DEFAULT_DUMP_FILE_CHMOD="0400"


# MySQL defaults
_DEFAULT_MYSQL_CNF_CHMOD="0400"

# Default minimum size (in MB) required to enable `--quick`
_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE=200

# Default OpenSSL Variables
_DEFAULT_OPENSSL_ALGO_ARG="-aes256"

# Logging defaults
_DEFAULT_LOG_CHMOD="0600"
_DEFAULT_LOG_FILE="${_PREFIX_LOG}/var/log/mysqldump-secure.log"

# Default nagios variables
_DEFAULT_NAGIOS_LOG_CHMOD="0644"

# Default system temp directory
_DEFAULT_TMP_DIR="/tmp"



################################################################################
#
# MYSQL VARIABLES
#
################################################################################


# These command line arguments are considered insecure and can lead
# to compromising your data
MDS_MYSQL_EVIL_OPTS="--password -p --port -P --user -u --host -h --socket -s"

# Do not allow to read any other MySQL configuration file
# than the one specified in the configuration.
MDS_MYSQL_BAD_OPTS="--no-defaults --defaults-extra-file --defaults-file"
# All SSL Options are done via configuration
MDS_MYSQL_BAD_OPTS="${MDS_MYSQL_BAD_OPTS} --ssl --ssl-ca --ssl-cert --ssl-key"
# All Transaction Options (Consistency) are done via configuration
MDS_MYSQL_BAD_OPTS="${MDS_MYSQL_BAD_OPTS} --single-transaction --lock-tables --skip-lock-tables"
# Handled conditionally
MDS_MYSQL_BAD_OPTS="${MDS_MYSQL_BAD_OPTS} --quick -q --skip-quick"



################################################################################
#
# TERMINAL COLORS
#
################################################################################

# If having a tty (console) use colors
if [ -t 1 ]; then
	CLR_FATAL="\033[0;31m"		# Red
	CLR_ERROR="\033[0;31m"		# Red
	CLR_WARNING="\033[0;33m"	# Yellow
	CLR_INFO=""
	CLR_OK="\033[0;32m"			# Green
	CLR_DEBUG="\033[0;37m"		# Gray
	CLR_TRACE="\033[0;37m"		# Gray
	CLR_RESET="\033[m"			# Normal
# Dump terminal (e.g.: via cron)
else
	CLR_FATAL=""
	CLR_ERROR=""
	CLR_WARNING=""
	CLR_INFO=""
	CLR_OK=""
	CLR_DEBUG=""
	CLR_TRACE=""
	CLR_RESET=""
fi





####################################################################################################
####################################################################################################
##
##  G L O B A L   F U N C T I O N S
##
####################################################################################################
####################################################################################################


#
# Naming convention:
# ------------------
# Functions starting with '_name' are considered private and should only
# be called from within other functions.
#



################################################################################
#
# ABORT
#
################################################################################

# This function is used to abort
# the whole script if internal errors occured.
# This function should never be called.
# If this function is called, the programmer screwed up.
#
# @param  string Calling function name (for reference)
# @param  string Affected variable name (for reference)
# @param  string Affected variable value (for reference)
# @output string Error message
# @exit   integer This function aborts with exit code
_abort_on_internal_error() {
	_func="${1}"
	_var="${2}"
	_val="${3}"

	echo "Unhandled exception in: ${_func}" 1>&2
	echo "Invalid value for ${_var}: '${_val}'" 1>&2
	echo "Please report this error on github: ${INFO_REPO}" 1>&2
	echo "The programmer fucked up" 1>&2
	echo "Good bye" 1>&2

	exit ${EXIT_INT_ERR}
}



################################################################################
#
#  L O G G I N G   F U N C T I O N S
#
################################################################################


############################################################
# Private Logger Helper
############################################################


# Get the severity prefix for stdout/stderr and logging
#
# @param  string  Severity
# @output string  Formatted level
# @return integer 0: success, 1: unknown level
_get_severity_level_prefix() {
	_lvl="${1}"

	case "${_lvl}" in
		trace)
			echo "[TRACE]"
			return 0
			;;
		debug)
			echo "[DEBUG]"
			return 0
			;;
		info)
			echo "[INFO] "
			return 0
			;;
		ok)
			echo "[OK]   "
			return 0
			;;
		warn)
			echo "[WARN] "
			return 0
			;;
		err)
			echo "[ERR]  "
			return 0
			;;
		fatal)
			echo "[FATAL]"
			return 0
			;;
		*)
			echo "[UNDEF]"
			return 1
			;;
	esac
}


# Get the severity color based on error level
#
# @param  string  Severity
# @output string  Color code
# @return integer 0: success, 1: unknown level
_get_severity_color_prefix() {
	_lvl="${1}"

	case "${_lvl}" in
		trace)
			echo "${CLR_TRACE}"
			return 0
			;;
		debug)
			echo "${CLR_DEBUG}"
			return 0
			;;
		info)
			echo "${CLR_INFO}"
			return 0
			;;
		ok)
			echo "${CLR_OK}"
			return 0
			;;
		warn)
			echo "${CLR_WARNING}"
			return 0
			;;
		err)
			echo "${CLR_ERROR}"
			return 0
			;;
		fatal)
			echo "${CLR_FATAL}"
			return 0
			;;
		*)
			echo ""
			return 1
			;;
	esac
}


#
# Output (single or multi-line) text to stdout, stderr or file
# (Console Out Recursive: _cout)
#
# @param  string  Where to output: 'stdout', 'stderr' or file
# @param  string  Line Prefix (e.g.: date or color)
# @param  string  Multi/single line message to outout
# @param  string  Line Suffix (e.g.: color resetter)
# @return integer Success
_coutr() {
	_target="${1}"
	_prefix="${2}"
	_string="${3}"
	_suffix="${4}"

	if [ "${_target}" = "stderr" ]; then
		IFS='
		'
		for _line in ${_string}; do
			printf "${_prefix}%s${_suffix}\n" "${_line}"  1>&2	# stdout -> stderr
		done
		unset IFS
		return 0
	elif  [ "${_target}" = "stdout" ]; then
		IFS='
		'
		for _line in ${_string}; do
			printf "${_prefix}%s${_suffix}\n" "${_line}" # stdout
		done
		unset IFS
		return 0
	elif [ -f "${_target}" ]; then
		IFS='
		'
		for _line in ${_string}; do
			printf "${_prefix}%s${_suffix}\n" "${_line}" >> "${_target}" # file
		done
		unset IFS
		return 0
	fi

	return 1
}


#
# Output single line to stdout, stderr or file.
# (Console Out Inline: _couti)
#
# Make sure to take care about trailing newlines inside $_string
#
# @param  string  Where to output: 'stdout', 'stderr' or file
# @param  string  Line Prefix (e.g.: date or color)
# @param  string  Single line message to outout
# @param  string  Line Suffix (e.g.: color resetter)
# @return integer Success
_couti() {
	_target="${1}"
	_prefix="${2}"
	_string="${3}"
	_suffix="${4}"

	if [ "${_target}" = "stderr" ]; then
		printf "${_prefix}${_string}${_suffix}%s" "" 1>&2	# stdout -> stderr
		return 0
	elif  [ "${_target}" = "stdout" ]; then
		printf "${_prefix}${_string}${_suffix}%s" "" # stdout
		return 0
	elif [ -f "${_target}" ]; then
		printf "${_prefix}${_string}${_suffix}%s" "" >> "${_target}" # file
		return 0
	fi

	return 1
}



############################################################
# Public Logger functions
############################################################

#
# Single- multi-line output to stdout/stderr and/or logfile.
#
# @public
#
# @param  string  _lvl	  Assigned log level: "trace", "debug", "info", "warn" or "err" "fatal"
#                         Can also be a combination separated with '|' for an extra prefix.
#                         E.g.: "err|(RUN)"
# @param  string  _msg	  Message (single-line or multiline)
# @param  integer _ver	  (0|1|2|3) Current set verbosity level (0:fat,err,warn, 1:normal, 2:debug, 3:trace)
# @param  string  _log	  (0|1|2|3) Current set file logging level (0:off, 1:normal, 2:debug, 3:trace)
# @param  integer _file   Full path to logfile
# @return integer 0
debug() {
	_lvl="${1}"		# Assigned log level: "trace", "debug", "info", "warn" or "err" "fatal"
	_msg="${2}"		# Message (single-line or multiline)
	_ver="${3}"		# (0|1|2|3) Current set verbosity level (0:fat,err,warn, 1:normal, 2:debug, 3:trace)
	_log="${4}"		# (0|1|2|3) Current set file logging level (0:off, 1:normal, 2:debug, 3:trace)
	_file="${5}"	# Full path to logfile


	# Separate _lvl which could be in format:
	# "err|(RUN): "
	_pre="$( echo "${_lvl}" | awk -F '|' '{print $2}' )" # Get prefix if it exists behind a '|' separator
	_lvl="$( echo "${_lvl}" | awk -F '|' '{print $1}' )" # Extract _lvl (must be 2nd line, as it overwrites itself)


	# Make sure this function is used correctly
	if [ "${#}" != "5" ]; then
		_abort_on_internal_error "debug()" "\$#" "${#}"
	fi
	if [ "${_lvl}" != "trace" ] && [ "${_lvl}" != "debug" ] && [ "${_lvl}" != "info" ] && [ "${_lvl}" != "ok" ] && [ "${_lvl}" != "err" ] && [ "${_lvl}" != "warn" ] && [ "${_lvl}" != "fatal" ]; then
		_abort_on_internal_error "debug()" "\$_lvl" "${_lvl}"
	fi
	if [ "${_ver}" != "0" ] && [ "${_ver}" != "1" ] && [ "${_ver}" != "2" ] && [ "${_ver}" != "3" ]; then
		_abort_on_internal_error "debug()" "\$_ver" "${_ver}"
	fi
	if [ "${_log}" != "0" ] && [ "${_log}" != "1" ] && [ "${_log}" != "2" ] && [ "${_log}" != "3" ]; then
		_abort_on_internal_error "debug()" "\$_log" "${_log}"
	fi


	# Prefixes for severity level and date
	_pre_level="$( _get_severity_level_prefix "${_lvl}" )"	# e.g.: "[DEBUG]" or [ERR], etc
	_pre_color="$( _get_severity_color_prefix "${_lvl}" )"	# e.g.: Red color code for error
	_pre_date="$( date '+%Y-%m-%d %H:%M:%S' )"


	# Remove empty lines and lines which only contain spaces from $_msg
	_msg="$( echo "${_msg}" | sed '/^[[:space:]]*$/d' )"


	#
	# ---------- [OUT] to stdout or stderr ----------
	#


	#
	# 1.) [OUT] Always output '[fatal], [error] and [warning] to stderr
	#
	if [ "${_lvl}" = "fatal" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "warn" ]; then
		_coutr "stderr" "${_pre_color}${_pre_level} ${_pre}" "${_msg}" "${CLR_RESET}"
	fi

	#
	# 2.) [OUT] Highest verbosity (Trace mode: -vv)
	#
	if [ "${_ver}" = "3" ]; then
		if [ "${_lvl}" = "trace" ] || [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_coutr "stdout" "${_pre_color}${_pre_level} ${_pre}" "${_msg}" "${CLR_RESET}"
		fi
	fi

	#
	# 3.) [OUT] Debug verbosity (Debug mode: -v)
	#
	if [ "${_ver}" = "2" ]; then
		if [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_coutr "stdout" "${_pre_color}${_pre_level} ${_pre}" "${_msg}" "${CLR_RESET}"
		fi
	fi

	#
	# 4.) [OUT] Normal verbosity (Normal mode)
	#
	if [ "${_ver}" = "1" ]; then
		if [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_coutr "stdout" "${_pre_color}${_pre_level} ${_pre}" "${_msg}" "${CLR_RESET}"
		fi
	fi


	#
	# ---------- [FILE] to logfile ----------
	#

	#
	# 5.) [FILE] Highest log level (LOG=3)
	#
	if [ "${_log}" = "3" ]; then
		_coutr "${_file}" "${_pre_date} ${_pre_level} ${_pre}" "${_msg}" ""
	fi

	#
	# 6.) [FILE] Debug log level (LOG=2)
	#
	if [ "${_log}" = "2" ]; then
		if [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ] || [ "${_lvl}" = "warn" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "fatal" ]; then
			_coutr "${_file}" "${_pre_date} ${_pre_level} ${_pre}" "${_msg}" ""
		fi
	fi

	#
	# 6.) [FILE] Normal log level (LOG=1)
	#
	if [ "${_log}" = "1" ]; then
		if [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ] || [ "${_lvl}" = "warn" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "fatal" ]; then
			_coutr "${_file}" "${_pre_date} ${_pre_level} ${_pre}" "${_msg}" ""
		fi
	fi

	return 0
}

#
# Single-line inline output (without prefix and without newline) to stdout/stderr and/or logfile.
#
# @public
#
# @param  string  _lvl	  Assigned log level: "trace", "debug", "info", "warn" or "err" "fatal"
# @param  string  _msg	  Message (single-line or multiline)
# @param  integer _ver	  (0|1|2|3) Current set verbosity level (0:fat,err,warn, 1:normal, 2:debug, 3:trace)
# @param  string  _log	  (0|1|2|3) Current set file logging level (0:off, 1:normal, 2:debug, 3:trace)
# @param  integer _file   Full path to logfile
# @return integer 0
debug_i() {
	_lvl="${1}"		# Assigned log level: "trace", "debug", "info", "warn" or "err" "fatal"
	_msg="${2}"		# Message (single-line or multiline)
	_ver="${3}"		# (0|1|2|3) Current set verbosity level (0:fat,err,warn, 1:normal, 2:debug, 3:trace)
	_log="${4}"		# (0|1|2|3) Current set file logging level (0:off, 1:normal, 2:debug, 3:trace)
	_file="${5}"	# Full path to logfile

	# Make sure this function is used correctly
	if [ "${#}" != "5" ]; then
		_abort_on_internal_error "debug_i()" "\$#" "${#}"
	fi
	if [ "${_lvl}" != "trace" ] && [ "${_lvl}" != "debug" ] && [ "${_lvl}" != "info" ] && [ "${_lvl}" != "ok" ] && [ "${_lvl}" != "err" ] && [ "${_lvl}" != "warn" ] && [ "${_lvl}" != "fatal" ]; then
		_abort_on_internal_error "debug_i()" "\$_lvl" "${_lvl}"
	fi
	if [ "${_ver}" != "0" ] && [ "${_ver}" != "1" ] && [ "${_ver}" != "2" ] && [ "${_ver}" != "3" ]; then
		_abort_on_internal_error "debug_i()" "\$_ver" "${_ver}"
	fi
	if [ "${_log}" != "0" ] && [ "${_log}" != "1" ] && [ "${_log}" != "2" ] && [ "${_log}" != "3" ]; then
		_abort_on_internal_error "debug_i()" "\$_log" "${_log}"
	fi



	# Prefixes for severity level and date
	_pre_color="$( _get_severity_color_prefix "${_lvl}" )"	# e.g.: Red color code for error

	# Prefix the line?
	_pre_date=""
	_pre_level=""


	# Remove empty lines and lines which only contain spaces from $_msg
	#_msg="$( echo "${_msg}" | sed '/^[[:space:]]*$/d' )"


	#
	# ---------- [OUT] to stdout or stderr ----------
	#


	#
	# 1.) [OUT] Always output '[fatal], [error] and [warning] to stderr
	#
	if [ "${_lvl}" = "fatal" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "warn" ]; then
		_couti "stderr" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
	fi

	#
	# 2.) [OUT] Highest verbosity (Trace mode: -vv)
	#
	if [ "${_ver}" = "3" ]; then
		if [ "${_lvl}" = "trace" ] || [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_couti "stdout" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
		fi
	fi

	#
	# 3.) [OUT] Debug verbosity (Debug mode: -v)
	#
	if [ "${_ver}" = "2" ]; then
		if [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_couti "stdout" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
		fi
	fi

	#
	# 4.) [OUT] Normal verbosity (Normal mode)
	#
	if [ "${_ver}" = "1" ]; then
		if [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_couti "stdout" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
		fi
	fi


	#
	# ---------- [FILE] to logfile ----------
	#

	#
	# 5.) [FILE] Highest log level (LOG=3)
	#
	if [ "${_log}" = "3" ]; then
		_couti "${_file}" "${_pre_date} ${_pre_level} " "${_msg}" ""
	fi

	#
	# 6.) [FILE] Debug log level (LOG=2)
	#
	if [ "${_log}" = "2" ]; then
		if [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ] || [ "${_lvl}" = "warn" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "fatal" ]; then
			_couti "${_file}" "${_pre_date} ${_pre_level} " "${_msg}" ""
		fi
	fi

	#
	# 6.) [FILE] Normal log level (LOG=1)
	#
	if [ "${_log}" = "1" ]; then
		if [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ] || [ "${_lvl}" = "warn" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "fatal" ]; then
			_couti "${_file}" "${_pre_date} ${_pre_level} " "${_msg}" ""
		fi
	fi

	return 0
}


#
# Single-line inline output with prefix (without newline) to stdout/stderr and/or logfile.
#
# @public
#
# @param  string  _lvl	  Assigned log level: "trace", "debug", "info", "warn" or "err" "fatal"
# @param  string  _msg	  Message (single-line or multiline)
# @param  integer _ver	  (0|1|2|3) Current set verbosity level (0:fat,err,warn, 1:normal, 2:debug, 3:trace)
# @param  string  _log	  (0|1|2|3) Current set file logging level (0:off, 1:normal, 2:debug, 3:trace)
# @param  integer _file   Full path to logfile
# @return integer 0
debug_pi() {
	_lvl="${1}"		# Assigned log level: "trace", "debug", "info", "warn" or "err" "fatal"
	_msg="${2}"		# Message (single-line or multiline)
	_ver="${3}"		# (0|1|2|3) Current set verbosity level (0:fat,err,warn, 1:normal, 2:debug, 3:trace)
	_log="${4}"		# (0|1|2|3) Current set file logging level (0:off, 1:normal, 2:debug, 3:trace)
	_file="${5}"	# Full path to logfile

	# Make sure this function is used correctly
	if [ "${#}" != "5" ]; then
		_abort_on_internal_error "debug_pi()" "\$#" "${#}"
	fi
	if [ "${_lvl}" != "trace" ] && [ "${_lvl}" != "debug" ] && [ "${_lvl}" != "info" ] && [ "${_lvl}" != "ok" ] && [ "${_lvl}" != "err" ] && [ "${_lvl}" != "warn" ] && [ "${_lvl}" != "fatal" ]; then
		_abort_on_internal_error "debug_pi()" "\$_lvl" "${_lvl}"
	fi
	if [ "${_ver}" != "0" ] && [ "${_ver}" != "1" ] && [ "${_ver}" != "2" ] && [ "${_ver}" != "3" ]; then
		_abort_on_internal_error "debug_pi()" "\$_ver" "${_ver}"
	fi
	if [ "${_log}" != "0" ] && [ "${_log}" != "1" ] && [ "${_log}" != "2" ] && [ "${_log}" != "3" ]; then
		_abort_on_internal_error "debug_pi()" "\$_log" "${_log}"
	fi


	# Prefixes for severity level and date
	_pre_color="$( _get_severity_color_prefix "${_lvl}" )"	# e.g.: Red color code for error

	_pre_date="$( date '+%Y-%m-%d %H:%M:%S' )"
	_pre_level="$( _get_severity_level_prefix "${_lvl}" )"	# e.g.: "[DEBUG]" or [ERR], etc


	# Remove empty lines and lines which only contain spaces from $_msg
	#_msg="$( echo "${_msg}" | sed '/^[[:space:]]*$/d' )"


	#
	# ---------- [OUT] to stdout or stderr ----------
	#


	#
	# 1.) [OUT] Always output '[fatal], [error] and [warning] to stderr
	#
	if [ "${_lvl}" = "fatal" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "warn" ]; then
		_couti "stderr" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
	fi

	#
	# 2.) [OUT] Highest verbosity (Trace mode: -vv)
	#
	if [ "${_ver}" = "3" ]; then
		if [ "${_lvl}" = "trace" ] || [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_couti "stdout" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
		fi
	fi

	#
	# 3.) [OUT] Debug verbosity (Debug mode: -v)
	#
	if [ "${_ver}" = "2" ]; then
		if [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_couti "stdout" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
		fi
	fi

	#
	# 4.) [OUT] Normal verbosity (Normal mode)
	#
	if [ "${_ver}" = "1" ]; then
		if [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ]; then
			_couti "stdout" "${_pre_color}${_pre_level} " "${_msg}" "${CLR_RESET}"
		fi
	fi


	#
	# ---------- [FILE] to logfile ----------
	#

	#
	# 5.) [FILE] Highest log level (LOG=3)
	#
	if [ "${_log}" = "3" ]; then
		_couti "${_file}" "${_pre_date} ${_pre_level} " "${_msg}" ""
	fi

	#
	# 6.) [FILE] Debug log level (LOG=2)
	#
	if [ "${_log}" = "2" ]; then
		if [ "${_lvl}" = "debug" ] || [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ] || [ "${_lvl}" = "warn" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "fatal" ]; then
			_couti "${_file}" "${_pre_date} ${_pre_level} " "${_msg}" ""
		fi
	fi

	#
	# 6.) [FILE] Normal log level (LOG=1)
	#
	if [ "${_log}" = "1" ]; then
		if [ "${_lvl}" = "info" ] || [ "${_lvl}" = "ok" ] || [ "${_lvl}" = "warn" ] || [ "${_lvl}" = "err" ] || [ "${_lvl}" = "fatal" ]; then
			_couti "${_file}" "${_pre_date} ${_pre_level} " "${_msg}" ""
		fi
	fi

	return 0
}




################################################################################
#
#  M A T H   F U N C T I O N S
#
################################################################################


# Test if argument is an integer.
#
# @param  mixed
# @return integer	0: is number | 1: not a number
isint(){
	printf "%d" "${1}" >/dev/null 2>&1 && return 0 || return 1;
}


# Sum up two numbers.
#
# @param  float/integer
# @param  float/integer
# @output float/integer
sum() {
	echo | awk -v "a=${1}" -v "b=${2}" '{print a + b}'
}

# Subtract two numbers.
#
# @param  float/integer
# @param  float/integer
# @output float/integer
sub() {
	echo | awk -v "a=${1}" -v "b=${2}" '{print a - b}'
}

# Divide two numbers.
#
# @param  float/integer
# @param  float/integer
# @output float/integer
div() {
	echo | awk -v "a=${1}" -v "b=${2}" '{print a / b}'
}



# Round a number
#
# @param  float	   Number to round
# @param  integer  Decimal places
# @output float    Result
round() {
	_number="${1}"
	_precision="${2}"

	# Note this is a locale dependent usage of printf
	# Separators are assumed '.' and not ','
	# This is fixed via setting LC_ALL=C and LANG=C
	# at the very top of this script.
	printf '%.*f\n' "${_precision}" "${_number}"
}


################################################################################
#
#  S T R I N G   F U N C T I O N S
#
################################################################################


#
# Get the length of a string.
#
# @param  string
# @output integer Length
str_len() {
	echo "${1}" | awk '{print length()}'
}


# Concatenate two strings with a glue (separator).
# Do not add separator if first string is empty.
#
# @param  string  First string
# @param  string  Glue
# @param  string  Second string
# @output string  Final string
str_join() {
	_first="${1}"
	_glue="${2}"
	_last="${3}"

	if [ "${_first}" = "" ]; then
		echo "${_last}";
		return 0
	elif [ "${_last}" = "" ]; then
		echo "${_first}";
		return 0
	fi

	# Make sure $_glue is within the placeholder part of printf
	# this way it can also use '\n' and make a real newline
	printf "%s${_glue}%s\n" "${_first}" "${_last}"
	return
}

# Renove a word from a space separated string.
#
# @param  string  Space separated string of words
# @param  string  Word to remove
# @output string  Final string
str_remove_word() {
	_string="${1}"
	_word="${2}"

	# Loop over each space separated word line by line
	for _each in ${_string}; do
		# Desired word found?
		# Remove from variable
		if [ "${_word}" = "${_each}" ]; then
			_string="$( echo "${_string}" | sed "s/${_word}//" )"
		fi
	done

	echo "${_string}"
}


# Get number of lines in a string
#
# @param  string  String
# @output integer Number of lines
str_count_lines() {
	_string="${1}"
	echo "${_string}" | grep -c ''
}




################################################################################
#
#  D A T E  F U N C T I O N S
#
################################################################################


# Timestamp to formated date.
#
# @param  integer  Timestamp
# @param  string   Format (e.g.: '%Y-%m-%d %H:%M:%S %Z [%z')
# @output string   Formated date
# @return integer  0:success, 1:fail
formatted_timestamp() {
	_timestamp="${1}"
	_format="${2}"

	# BSD / OSX date function
	if date -r 1457171267 >/dev/null 2>&1; then
		date -r "${_timestamp}" +"${_format}"
		return 0
	# Linux date
	elif date -d @1457171267 >/dev/null 2>&1; then
		date -d @"${_timestamp}" +"${_format}"
		return 0
	fi
	echo "${_timestamp}"

	return 1
}


# Get time in sec since unix epoch
#
# @output integer Time in Seconds
#
get_time() {
	date +%s
}

# Calculate duration in seconds
#
# @param  integer Unix epoch start time (in seconds)
# @output integer Duration in seconds
get_duration() {
	_start_time="${1}"
	_end_time="$(get_time)"

	_duration="$((_end_time - _start_time))"
	echo "${_duration}"
}


################################################################################
#
#  F I L E   F U N C T I O N S
#
################################################################################



# Get the file mtime
# When file is modified
#
# @param  string	path to file/folder
# @output integer	File mtime (timestamp)
# @return integer	Return code
get_file_mtime() {
	_file="${1}"
	_mtime="0"
	_exit=0

	# Linux version
	if ! _mtime="$(stat -c %Y "${_file}" 2>/dev/null)"; then
		# OSX/BSD version
		if ! _mtime="$(stat -f %m "${_file}" 2>/dev/null)"; then
			# Try date
			if ! _mtime="$(date -r "${_file}" +%s 2>/dev/null)"; then
				_mtime="0"
				_exit=1
			fi
		fi
	fi

	echo "${_mtime}"
	return ${_exit}
}


# Get the file ctime
# When file is modified or when inode changes (chown/chmod)
#
# @param  string	path to file/folder
# @output integer	File mtime (timestamp)
# @return integer	Return code
get_file_ctime() {
	_file="${1}"
	_ctime="0"
	_exit=0

	# Linux version
	if ! _ctime="$(stat -c %Z "${_file}" 2>/dev/null)"; then
		# OSX/BSD version
		if ! _ctime="$(stat -f %c "${_file}" 2>/dev/null)"; then
			_ctime="0"
			_exit=1
		fi
	fi

	echo "${_ctime}"
	return ${_exit}
}



# Get file size in bytes of file or directory.
#
# @param  string	path to file/folder
# @param  char	    Unit: 'b': Bytes, 'k': KB, 'm': MB, 'g': GB (defaults to 'b')
# @output integer	File size in bytes
# @return integer	Return code
get_file_size() {
	_file="${1}"
	_unit="${2}"
	_size="0"
	_exit=1

	_size="$($(which ls) -l "${_file}" 2>/dev/null)"
	_exit=$?
	_size="$(echo "${_size}" | awk '{print $5}')"

	if [ "${_unit}" = "k" ]; then
		_size="$( div "${_size}" "1024" )"
		_size="$( round "${_size}" "2" )"
	elif [ "${_unit}" = "m" ]; then
		_size="$( div "${_size}" "1048576" )"
		_size="$( round "${_size}" "2" )"
	elif [ "${_unit}" = "g" ]; then
		_size="$( div "${_size}" "1073741824" )"
		_size="$( round "${_size}" "2" )"
	fi

	echo "${_size}"
	return ${_exit}
}


# Get md5/sha256 hash of file or directory.
#
# @param  string	path to file/folder
# @param  string	algo: 'md5' or 'sha256' (defaults to md5)
# @output string	Hash
# @return integer	Return code
get_file_hash() {
	_file="${1}"
	_algo="${2}"
	_hash="unknown"
	_exit=1

	if [ "${_algo}" = "sha256" ]; then
		if command -v shasum > /dev/null 2>&1 ; then
			_hash="$($(which shasum) -a 256 "${_file}" 2>/dev/null)"
			_exit=$?
			_hash="$(echo "${_hash}" | awk '{print $1}')"
		elif command -v sha256sum > /dev/null 2>&1 ; then
			_hash="$($(which sha256sum) "${_file}" 2>/dev/null)"
			_exit=$?
			_hash="$(echo "${_hash}" | awk '{print $1}')"
		fi
	else
		if command -v md5 > /dev/null 2>&1 ; then
			_hash="$($(which md5) "${_file}" 2>/dev/null)"
			_exit=$?
			_hash="$(echo "${_hash}" | awk -F '=' '{print $2}' | awk '{print $1}')"
		elif command -v md5sum > /dev/null 2>&1 ; then
			_hash="$($(which md5sum) "${_file}" 2>/dev/null)"
			_exit=$?
			_hash="$(echo "${_hash}" | awk '{print $1}')"
		fi
	fi

	echo "${_hash}"
	return ${_exit}
}


# Get Operating system independent numerical 4-digit chmod value.
#
# @param  string	path to file/folder
# @output string	numerical chmod (4-digit format)
# @return integer	Return code
get_file_chmod() {
	_file="${1}"
	_perm="000"
	_exit=1

	# e.g. 640
	if [ "$(uname)" = "Linux" ]; then
		# If no special permissions are set (no sticky bit...), linux will
		# only output the 3 digit number
		_perm="$(stat --format '%a' "${_file}" 2>/dev/null)"
		_exit=$?
	else # Darwin or FreeBSD, OpenBSD, NetBSD
		_perm="$(stat -f "%Mp%Lp" "${_file}" 2>/dev/null)"
		_exit=$?
	fi

	# For special cases check the length and add a leading 0
	_len="$(echo "${_perm}" | awk '{ print length() }')"
	if [ "${_len}" = "3" ]; then
		_perm="0${_perm}"
	fi

	echo "${_perm}"
	return ${_exit}
}


# Get Operating system independent owner of file
#
# @param  string	path to file/folder
# @output string	Owner
# @return integer	Return code
get_file_owner() {
	_file="${1}"
	_owner="unknown"
	_exit=0

	# Linux version
	if ! _owner="$(stat -c %U "${_file}" 2>/dev/null)"; then
		# OSX/BSD version
		if ! _owner="$(stat -f %Su "${_file}" 2>/dev/null)"; then
			_owner="unknown"
			_exit=1
		fi
	fi

	echo "${_owner}"
	return ${_exit}
}

# Get Operating system independent group of file
#
# @param  string	path to file/folder
# @output string	Owner
# @return integer	Return code
get_file_group() {
	_file="${1}"
	_group="unknown"
	_exit=0

	# Linux version
	if ! _group="$(stat -c %G "${_file}" 2>/dev/null)"; then
		# OSX/BSD version
		if ! _group="$(stat -f %Sg "${_file}" 2>/dev/null)"; then
			_group="unknown"
			_exit=1
		fi
	fi

	echo "${_group}"
	return ${_exit}
}


# Get the real path of a file/folder if it is a symlink
#
# @param  string	path (file, folder or symlink)
# @output string	New path
# @return integer	Success
get_file_realpath() {
	_file="${1}"
	_exit=1

	_path="$(realpath "${_file}" 2>/dev/null)"
	_exit=$?

	# If path not found, return the original specified path
	if [ "${_exit}" != "0" ]; then
		_path="${_file}"
	fi

	echo "${_path}"
	return "${_exit}"
}

# Create a temporary directory using mktemp
#
# @param  string	Path where the file should be created
# @param  string	Chmod of file
# @output string	Full path of the created temp file
# @return integer	Success
create_tmp_dir() {
	_path="${1}"
	_chmod="${2}"

	umask "$(chmod_2_umask "${_chmod}")";
	if ! _dir="$( $(which mktemp) -d "${_path}" 2>/dev/null )"; then
		return $?
	else
		echo "${_dir}"
	fi

	return 0
}


# Create a temporary file using mktemp
#
# @param  string	Path where the file should be created
# @param  string	Chmod of file
# @output string	Full path of the created temp file
# @return integer	Success
create_tmp_file() {
	_path="${1}"
	_chmod="${2}"

	umask "$(chmod_2_umask "${_chmod}")";
	if ! _file="$( $(which mktemp) "${_path}" 2>/dev/null )"; then
		return $?
	else
		echo "${_file}"
	fi

	return 0
}



################################################################################
#
#  C H M O D   F U N C T I O N S
#
################################################################################


# Validate chmod value.
#
# @param  string	numerical chmod
# @return boolean	0: valid, 1: invalid
valid_chmod() {
	_chmod="${1}"
	_len="$(echo "${_chmod}" | awk '{ print length() }')"


	if [ "${_len}" = "3" ]; then

		r="$(echo "${_chmod}" | awk '{print substr($0,1,1)}')"
		w="$(echo "${_chmod}" | awk '{print substr($0,2,1)}')"
		x="$(echo "${_chmod}" | awk '{print substr($0,3,1)}')"


		# Check if it is an integer
		if ! isint "${r}" > /dev/null 2>&1 ; then
			return 1
		elif ! isint "${w}" > /dev/null 2>&1 ; then
			return 1
		elif ! isint "${x}" > /dev/null 2>&1 ; then
			return 1
		fi

		# Error out
		if [ "$r" -lt 0 ] || [ "$r" -gt 7 ]; then
			return 1
		fi
		if [ "$w" -lt 0 ] || [ "$w" -gt 7 ]; then
			return 1
		fi
		if [ "$x" -lt 0 ] || [ "$x" -gt 7 ]; then
			return 1
		fi
		# All good
		return 0

	elif  [ "${_len}" = "4" ]; then

		s="$(echo "${_chmod}" | awk '{print substr($0,1,1)}')"
		r="$(echo "${_chmod}" | awk '{print substr($0,2,1)}')"
		w="$(echo "${_chmod}" | awk '{print substr($0,3,1)}')"
		x="$(echo "${_chmod}" | awk '{print substr($0,4,1)}')"

		# Check if it is an integer
		if ! isint "${s}" > /dev/null 2>&1 ; then
			return 1
		elif ! isint "${r}" > /dev/null 2>&1 ; then
			return 1
		elif ! isint "${w}" > /dev/null 2>&1 ; then
			return 1
		elif ! isint "${x}" > /dev/null 2>&1 ; then
			return 1
		fi

		# Error out
		if [ "$s" -lt 0 ] || [ "$s" -gt 7 ]; then
			return 1
		fi
		if [ "$r" -lt 0 ] || [ "$r" -gt 7 ]; then
			return 1
		fi
		if [ "$w" -lt 0 ] || [ "$w" -gt 7 ]; then
			return 1
		fi
		if [ "$x" -lt 0 ] || [ "$x" -gt 7 ]; then
			return 1
		fi
		# All good
		return 0

	else
		# Nope!
		return 1
	fi
}


# Compare two chmod values.
#
# @param  string	numerical chmod (3 or 4 digits)
# @param  string	numerical chmod (3 or 4 digits)
# @return boolean	0: equals, 1: unequal
compare_chmod() {
	_chmod1="${1}"
	_chmod2="${2}"

	_len1="$(echo "${_chmod1}" | awk '{ print length() }')"
	_len2="$(echo "${_chmod2}" | awk '{ print length() }')"

	if [ "${_len1}" = "${_len2}" ]; then
		if [ "${_chmod1}" = "${_chmod2}" ]; then
			return 0
		else
			return 1
		fi
	elif [ "${_len1}" = "3" ] && [ "${_len2}" = "4" ]; then
		if [ "0${_chmod1}" = "${_chmod2}" ]; then
			return 0
		else
			return 1
		fi
	elif [ "${_len1}" = "4" ] && [ "${_len2}" = "3" ]; then
		if [ "${_chmod1}" = "0${_chmod2}" ]; then
			return 0
		else
			return 1
		fi
	else
		return 1
	fi
}


# Convert 3-digit or 4-digit chmod to 3-digit umask
#
# @param  string	numerical chmod (3 or 4 digit)
# @output string	3-digit umask
# @return integer	0
chmod_2_umask() {
	_chmod="${1}"
	_len="$(echo "${_chmod}" | awk '{print length()}')"

	if [ "${_len}" = "3" ]; then
		r=$(echo "${_chmod}" | awk '{print substr($0,1,1)}')
		w=$(echo "${_chmod}" | awk '{print substr($0,2,1)}')
		x=$(echo "${_chmod}" | awk '{print substr($0,3,1)}')
	elif  [ "${_len}" = "4" ]; then
		r=$(echo "${_chmod}" | awk '{print substr($0,2,1)}')
		w=$(echo "${_chmod}" | awk '{print substr($0,3,1)}')
		x=$(echo "${_chmod}" | awk '{print substr($0,4,1)}')
	fi

	ur="$(sub "7" "${r}")"
	uw="$(sub "7" "${w}")"
	ux="$(sub "7" "${x}")"

	echo "${ur}${uw}${ux}"
}




################################################################################
#
#  T M P W A T C H   F U N C T I O N S
#
################################################################################


############################################################
# tmpwatch/tmpreaper checker/helper
############################################################

# Check for correct tmpwatch/tmpreaper value
#
# @param  string    tmpwatch/tmpreaper deletion value (1, 1m, 1h, 1d)
# @return integer   0: success, 1: failure
valid_tmpwatch() {
	_val="${1}"

	# No unit (auto-defaults to hours)
	if echo "${_val}" | grep -Eq '^[0-9]+$'; then
		if [ "${_val}" -gt 0 ]; then
			return 0
		fi
	# 'm' for minutes
	elif echo "${_val}" | grep -Eq '^[0-9]+m$'; then
		_num="$(echo "${_val}" | sed 's/m//g')"
		if [ "${_num}" -gt 0 ]; then
			return 0
		fi
	# 'h' for hours
	elif echo "${_val}" | grep -Eq '^[0-9]+h$'; then
		_num="$(echo "${_val}" | sed 's/h//g')"
		if [ "${_num}" -gt 0 ]; then
			return 0
		fi
	# 'd' for days
	elif echo "${_val}" | grep -Eq '^[0-9]+d$'; then
		_num="$(echo "${_val}" | sed 's/d//g')"
		if [ "${_num}" -gt 0 ]; then
			return 0
		fi
	fi

	# Fuck off
	return 1
}

# Get numerical value for tmpwatch/tmpreaper deletions
#
# @param  string    tmpwatch/tmpreaper deletion value (1, 1m, 1h, 1d)
# @output integer   Value
# @return integer	Success
get_tmpwatch_value() {
	_val="${1}"

	# No unit (auto-defaults to hours)
	if echo "${_val}" | grep -Eq '^[0-9]+$'; then
		echo "${_val}"
		return 0
	# 'm' for minutes
	elif echo "${_val}" | grep -Eq '^[0-9]+m$'; then
		echo "${_val}" | sed 's/m//g'
		return 0
	# 'h' for hours
	elif echo "${_val}" | grep -Eq '^[0-9]+h$'; then
		echo "${_val}" | sed 's/h//g'
		return 0
	# 'd' for days
	elif echo "${_val}" | grep -Eq '^[0-9]+d$'; then
		echo "${_val}" | sed 's/d//g'
		return 0
	else
		echo "Internal Error: [get_tmpwatch_value] Invalid value for \$_val: '${_val}'"
		echo "Please report this at github."
		exit 1
	fi
}

# Get human readable unit for tmpwatch/tmpreaper deletions (plural or singular)
#
# @param  string    tmpwatch/tmpreaper deletion value (1, 1m, 1h, 1d)
# @return string    Human readable unit
get_tmpwatch_unit_name() {
	_val="${1}"

	# No unit (auto-defaults to hours)
	if echo "${_val}" | grep -Eq '^[0-9]+$'; then
		if [ "${_val}" -gt 1 ]; then
			echo "hours"
		else
			echo "hour"
		fi
	# 'm' for minutes
	elif echo "${_val}" | grep -Eq '^[0-9]+m$'; then
		_num="$(echo "${_val}" | sed 's/m//g')"
		if [ "${_num}" -gt 1 ]; then
			echo "minutes"
		else
			echo "minute"
		fi
	# 'h' for hours
	elif echo "${_val}" | grep -Eq '^[0-9]+h$'; then
		_num="$(echo "${_val}" | sed 's/h//g')"
		if [ "${_num}" -gt 1 ]; then
			echo "hours"
		else
			echo "hour"
		fi
	# 'd' for days
	elif echo "${_val}" | grep -Eq '^[0-9]+d$'; then
		_num="$(echo "${_val}" | sed 's/d//g')"
		if [ "${_num}" -gt 1 ]; then
			echo "days"
		else
			echo "day"
		fi
	else
		echo "Internal Error: [get_tmpwatch_unit_name] Invalid value for \$_val: '${_val}'"
		echo "Please report this at github."
		exit 1
	fi
}



################################################################################
#
#  N A G I O S   F U N C T I O N S
#
################################################################################


# Aggregate nagios exit code.
#
# OK < Warning < Error < Unknown
# @param  integer The current exit code.
# @param  integer The new exit code
# @output integer The combined exit code
# @return integer The combined exit code
merge_exit_codes() {
	_curr_exit="${1}"
	_next_exit="${2}"

	# OK
	if [ "${_curr_exit}" = "0" ]; then
		_curr_exit="${_next_exit}"
	# Warning
	elif [ "${_curr_exit}" = "1" ]; then
		if [ "${_next_exit}" = "0" ]; then
			_curr_exit="1"
		elif [ "${_next_exit}" = "1" ]; then
			_curr_exit="1"
		elif [ "${_next_exit}" = "2" ]; then
			_curr_exit="2"
		elif [ "${_next_exit}" = "3" ]; then # UNKNOWN -> WARNING
			_curr_exit="1"
		fi
	# Error
	elif [ "${_curr_exit}" = "2" ]; then
		_curr_exit="2"
	# Unknown
	elif [ "${_curr_exit}" = "3" ]; then
		if [ "${_next_exit}" = "0" ]; then
			_curr_exit="3"
		elif [ "${_next_exit}" = "1" ]; then
			_curr_exit="1"
		elif [ "${_next_exit}" = "2" ]; then
			_curr_exit="2"
		elif [ "${_next_exit}" = "3" ]; then # UNKNOWN -> WARNING
			_curr_exit="3"
		fi
	fi
	echo "${_curr_exit}"
	return ${_curr_exit}
}





################################################################################
#
#  M Y S Q L   F U N C T I O N S
#
################################################################################


############################################################
# MySQL Connection Helper
############################################################


# Test if connection to the server is successful.
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output string  Error message
# @return integer Success (0: OK | 1: Error)
mysql_test_connection() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_error=""
	_errno=0

	# Redirect stderr into stdout and both into variable
	_error="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" -e exit 2>&1 )"
	_errno=$?

	if [ "${_errno}" != "0" ] || [ "${_error}" != "" ]; then
		echo "${_error}"
		return 1
	else
		return 0
	fi
}


############################################################
# MySQL Server Info Helper
############################################################

# Get MySQL server status 'status;'
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output string  MySQL 'status;'
# @return integer Success (0: OK | 1: Error)
get_mysql_server_status() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_exit=1

	_query="status;"
	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_exit=$?

	echo "${_result}"
	return ${_exit}
}

# Get MySQL server hostname
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output string  Hostname
# @return integer Success (0: OK | 1: Error)
get_mysql_server_hostname() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_exit=1

	_query="SHOW GLOBAL VARIABLES LIKE 'HOSTNAME';"

	# Redirect stderr into stdout and both into variable
	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_exit=$?

	_result="$( echo "${_result}" | tail -n1 | sed 's/hostname[[:space:]]*//g' )"

	echo "${_result}"
	return ${_exit}
}

# Get MySQL server port
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output string  Port
# @return integer Success (0: OK | 1: Error)
get_mysql_server_port() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_exit=1

	_query="SHOW GLOBAL VARIABLES LIKE 'PORT';"

	# Redirect stderr into stdout and both into variable
	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_exit=$?

	_result="$( echo "${_result}" | tail -n1 | sed 's/port[[:space:]]*//g' )"

	echo "${_result}"
	return ${_exit}
}

# Determines wheter the server connected to
# is
# * a master without replication (single)
# * a master with replication (master)
# * a slave
# * or unknown
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output string  Type (unknown|master|slave)
# @return integer Success (0: OK | 1: Error)
get_mysql_server_replication_type() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_exit=1

	_query="select COUNT(1) SlaveThreads from information_schema.processlist where user = 'system user';"
	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_exit=$?

	_result="$( echo "${_result}" | tail -n1 )"

	if [ "${_exit}" != "0" ]; then
		echo "unknown"
		return ${_exit}
	else
		if [ "${_result}" = "0" ]; then
			echo "master"
			return ${_exit}
		elif  [ "${_result}" -gt "0" ]; then
			echo "slave"
			return ${_exit}
		else
			echo "unknown"
			return 1
		fi
	fi
}

# Get MySQL Server Version
#
# @param  string  MySQL server 'status;' output
# @output string  Server Version
# @return integer 0
get_mysql_server_version() {
	_mysql_server_status="${1}"

	echo "${_mysql_server_status}" | grep -E '^Server version:.*' | sed 's/Server version:[[:space:]]*//g'
	return 0
}

# Get MySQL Server Name (MySQL or MariaDB)
#
# @param  string  MySQL server 'status;' output
# @output string  Server name
# @return integer 0
get_mysql_server_name() {
	_mysql_server_status="${1}"

	echo "${_mysql_server_status}" | grep -E '^Server:.*' | sed 's/Server:[[:space:]]*//g'
	return 0
}


############################################################
# MySQL Connection Info Helper
############################################################

# Get MySQL connection info (TCP/IP or Socket)
#
# @param  string  MySQL server 'status;' output
# @output string  Connection type (TCP/IP or Socket)
# @return integer 0
get_mysql_connection_type_info() {
	_mysql_server_status="${1}"

	echo "${_mysql_server_status}" | grep -E '^Connection:.*' | sed 's/Connection:[[:space:]]*//g'
	return 0
}

# Get MySQL connection info about SSL status
#
# @param  string  MySQL server 'status;' output
# @output string  SSL Connection info
# @return integer 0
get_mysql_connection_ssl_info() {
	_mysql_server_status="${1}"

	echo "${_mysql_server_status}" | grep -E '^SSL:.*' | sed 's/SSL:[[:space:]]*//g'
	return 0
}



############################################################
# MySQL Database Helper
############################################################


# Get all databas names
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output string  All databases (including empty ones)
# @return integer Success (0: OK | 1: Error)
get_mysql_databases() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_exit=1

	_query="show databases;"
	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_exit=$?

	# Only gets databases which have content (tables)
	#DATABASES="$( ${MYSQL} --defaults-file="${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" --no-auto-rehash --skip-column-names -e 'SELECT DISTINCT table_schema FROM information_schema.tables;')"

	# Remove first column (is sql column name)
	_result="$( echo "${_result}" | sed 1d )"

	# output all databases on success
	if [ "${_exit}" = "0" ]; then
		echo "${_result}"
	fi

	return "${_exit}"
}

# Get database size in btyes
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @output integer Database size in bytes
# @return integer Success (0: OK | 1: Error)
get_mysql_database_size() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_database="${3}"
	_exit=1

	_query="SELECT SUM(DATA_LENGTH + INDEX_LENGTH) AS Size
				FROM INFORMATION_SCHEMA.TABLES
				WHERE TABLE_SCHEMA = '${_database}';"


	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_exit=$?

	_result="$( echo "${_result}" | tail -n1 )"

	echo "${_result}"
	return ${_exit}
}

# Determines whether database is empty
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @return integer 0: Empty | 1: Not empty
mysql_database_is_empty() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_database="${3}"


	# If a database does not show up in information_schema.tables, it means it
	# has no tables yet and is therefore empty.
	# If count == 0, return '0', else '1'
	_query="SELECT IF(COUNT(*) = 0, '0', '1') AS empty
				FROM INFORMATION_SCHEMA.TABLES
				WHERE TABLE_SCHEMA = '${_database}';"

	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_result="$( echo "${_result}" | tail -n1 )"

	return "${_result}"
}

############################################################
# Table Helper Helper
############################################################

# Count all InnoDB tables
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @param  string  Database.
count_innodb_tables() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_database="${3}"
	_exit=1

	_query="SELECT COUNT(TABLE_NAME) AS cnt
				FROM information_schema.TABLES
				WHERE ENGINE = 'InnoDB'
				AND TABLE_SCHEMA = '${_database}';"

	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_result="$( echo "${_result}" | tail -n1 )"

	echo "${_result}"
}

# Count all InnoDB tables
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @param  string  Database.
count_non_innodb_tables() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_database="${3}"
	_exit=1

	_query="SELECT COUNT(TABLE_NAME) AS cnt
				FROM information_schema.TABLES
				WHERE ENGINE <> 'InnoDB'
				AND TABLE_SCHEMA = '${_database}';"

	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_result="$( echo "${_result}" | tail -n1 )"

	echo "${_result}"
}

# Count all tables
#
# @param  string  Credentials file (mysqldump-secure.cnf).
# @param  string  SSL connection options.
# @param  string  Database.
count_total_tables() {
	_cnf_file="${1}"
	_ssl_opts="${2}"
	_database="${3}"
	_exit=1

	_query="SELECT COUNT(TABLE_NAME) AS cnt
				FROM information_schema.TABLES
				WHERE TABLE_SCHEMA = '${_database}';"

	_result="$( $(which mysql) --defaults-file="${_cnf_file}" "${_ssl_opts}" --batch -e "${_query}" 2>/dev/null)"
	_result="$( echo "${_result}" | tail -n1 )"

	echo "${_result}"
}



################################################################################
#
#  D A T A B A S E   I G N O R E / R E Q U I R E   H E L P E R
#
################################################################################

# Check if the specified database should be ignored
# This can either be by the full name or a wildcard with '*'
#
# @param  string  Ignore list
# @param  string  Database name
# @output string  Ignore pattern
# @return integer 0: ignored, 1: not ignored
database_is_ignored() {
	_ignore_list="${1}"
	_database="${2}"

	# Default exit with 1, which means
	# it was not found.
	_exit=1

	# The ignore pattern if it was ignored
	_pattern=""

	# We need to disable "variable globbing" (set -f) here,
	# because if there is only a '*' inside '$IGNORE'
	# then
	#       'for ign_db in ${IGNORE}; do'
	# would list all files in the current directory.
	set -f
	for _ignore_pattern in ${_ignore_list}; do

		# Replace '*' with '.*' to create a regex pattern
		_regex="$(echo "${_ignore_pattern}" | sed 's/\*/\.\*/')"

		# Check if the pattern matches the complete db name
		_found="$(echo "${_database}" | grep -oe "${_regex}")"

		if [ "${_found}" = "${_database}" ]; then

			# Assign new ignore pattern
			_pattern="${_ignore_pattern}"

			# Return with 0 to indicate it was found
			_exit=0

			# Stop the loop
			break
		fi
	done
	set +f

	echo "${_pattern}"
	return ${_exit}
}

# Check if the specified database is required.
#
# @param  string  Require list
# @param  string  Database name
# @return integer 0: required, 1: not required
database_is_required() {
	_require_list="${1}"
	_database="${2}"

	# Now check if the ignored db is actually a required db inside $REQUIRE
	for _db_required in ${_require_list}; do
		# Required database found?
		if [ "${_database}" = "${_db_required}" ]; then
			# Returning 0 indicates it is required
			return 0
		fi
	done

	# Returning invalid return code indicates it is not required
	return 1

}


################################################################################
#
#  M I S C   F U N C T I O N S
#
################################################################################




# Determine the file extension
# based on encrypted/compressed files
#
# @param  integer _encrypt       Encryption enabled? (0: off | 1: on)
# @param  integer _compress      Compression enabled? (0: off | 1: on)
# @param  string  _compress_ext  Compression extension (e.g.: gz)
# @output string   File extension
build_file_extension() {
	_encrypt="${1}"
	_compress="${2}"
	_compress_ext="${3}"

	# Get file extension
	if [ "${_compress}" = "1" ]; then
		if [ "${_encrypt}" = "1" ]; then
			echo ".sql.${_compress_ext}.enc"
		else
			echo ".sql.${_compress_ext}"
		fi
	else
		if [ "${_encrypt}" = "1" ]; then
			echo ".sql.enc"
		else
			echo ".sql"
		fi
	fi
}



# Validate mysqldump-secure.cnf file
#
# The only allowed directives are:
#   [client]
#   socket =
#   host =
#   port =
#   user =
#   password =
# "[client]" must also be the first non-commented
# directive.
#
# @param  string  mysqldump-secure.cnf file path
# @output string  Error message
# @return integer 0: valid | 1: invalid
validate_cnf_file() {
	_file_path="${1}"

	# Get contents
	cnf="$(cat "${_file_path}")"

	# Remove line comments ('#' and ';')
	cnf="$(echo "${cnf}" | sed 's/^[[:space:]]*#.*$//g')"
	cnf="$(echo "${cnf}" | sed 's/^[[:space:]]*;.*$//g')"

	# Remove empty lines
	cnf="$(echo "${cnf}" | sed '/^$/d')"

	# Remove leading spaces
	cnf="$(echo "${cnf}" | sed 's/^[[:space:]]*//g')"

	# Remove trailing spaces
	cnf="$(echo "${cnf}" | sed 's/[[:space:]]*$//g')"

	IFS='
	'
	i=0
	set -f
	for _line in ${cnf}; do
		i=$((i + 1))

		# Everything before the '=' is the actual directive to check against
		# E.g: "host = localhost" results in "host"
		_directive="$(echo "${_line}" | awk -F '=' '{print $1}' | sed 's/^[[:space:]]*//g' | sed 's/[[:space:]]*$//g')"

		# Check for '[client]' in the first non-commented line
		if [ "${i}" = "1" ] && [ "${_line}" != "[client]" ]; then
			echo "The first non-commented line must contain a \"[client]\" directive"
			return 1
		fi

		# Only allow the following directives
		if  [ "${_directive}" != "[client]" ] &&
			[ "${_directive}" != "socket" ] &&
			[ "${_directive}" != "host" ] &&
			[ "${_directive}" != "port" ] &&
			[ "${_directive}" != "user" ] &&
			[ "${_directive}" != "password" ]; then
			echo "Invalid directive found: \"${_directive}\""
			return 1
		fi

	done
	set +f
	unset IFS

	# All fine, nothing bad found.
	return 0
}



################################################################################
#
#  M Y S Q L D U M P   F U N CT I O N S
#
################################################################################

# Get mysqldump consistency options based on database and settings.
#
# @param  integer  Settings for InnoDB only Database
# @param  integer  Settings for mixed InnoDB and others Database
# @param  integer  Settings for non-InnoDB Database
# @param  integer  How many InnoDB tables
# @param  integer  How many non-InnoDB tables
# @output string   Mysqldump consistency options
# @return integer  0
get_consistency_opts() {
	_consistent_dump_only_innodb="${1}"
	_consistent_dump_mixed_innodb="${2}"
	_consistent_dump_no_innodb="${3}"
	_tbl_cnt_innodb="${4}"
	_tbl_cnt_others="${5}"


	# Default is to ignore locks completely
	_lock_opts="--skip-lock-tables"

	#
	# Case 1: Only InnoDB Tables
	#
	if [ "${_tbl_cnt_others}" = "0" ]; then
		# Apply consistency to InnoDB
		if [ "${_consistent_dump_only_innodb}" = "1" ]; then
			_lock_opts="--single-transaction"
		else
			_lock_opts="--skip-lock-tables"
		fi
	#
	# Case 2: InnoDB + Other Tables
	#
	elif [ "${_tbl_cnt_innodb}" != "0" ] && [ "${_tbl_cnt_others}" != "0" ]; then
		# Apply consistency to InnoDB
		if [ "${_consistent_dump_mixed_innodb}" = "1" ]; then
			_lock_opts="--lock-tables"
		elif [ "${_consistent_dump_mixed_innodb}" = "2" ]; then
			_lock_opts="--single-transaction"
		else
			_lock_opts="--skip-lock-tables"
		fi
	#
	# Case 3: No InnoDB Tables
	#
	elif [ "${_tbl_cnt_innodb}" = "0" ]; then
		# Apply consistency to InnoDB
		if [ "${_consistent_dump_no_innodb}" = "1" ]; then
			_lock_opts="--lock-tables"
		else
			_lock_opts="--skip-lock-tables"
		fi
	#
	# Case 4: FALLBACK
	#
	else
		_lock_opts="--skip-lock-tables"
	fi

	echo "${_lock_opts}"
	return 0
}

# Run the actualy mysqldump here
#
# @param  integer  Dump mode (0: plain, 1: compressed, 2: encrypted, 3: compressed & encrypted)
# @param  string   MySQL connection args (cnf file and SSL arguments)
# @param  string   Database name
# @param  string   Destination file full path
# @param  integer  Destination file chmod
# @param  string   Name of compression binary (e.g.: gzip)
# @param  string   Compression arguments (e.g.: -9)
# @param  string   OpenSSL asymmetric encryption algorithm (e.g.: -aes256)
# @param  string   OpenSSL full path to public key file
# @param  string   Full path to temporary directory
# @output string   Multiline string of pipefail errors/warnings/success
# @return integer  0
run_mysqldump() {
	_dump_mode="${1}"			# 0: plain, 1: compressed, 2: encrypted, 3: compressed & encrypted
	_mysql_arg="${2}"			# mysql connection args, ssl and mysqldump arguments
	_database="${3}"			# which database to dump
	_file_name="${4}"
	_file_chmod="${5}"
	_compress_bin="${6}"
	_compress_arg="${7}"
	_openssl_algo="${8}"
	_openssl_cert="${9}"
	_tmp_dir="${10}"


	_error_statuses=""

	#
	# 0: Plain
	#
	if [ "${_dump_mode}" = "0" ]; then

		# Temp file 'mysqldump'
		_err_tmp_file_dump=""
		_ern_tmp_file_dump="0"
		if ! _tmp_file_dump="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_dump="Could not create tmp file in ${_tmp_dir}"
			_ern_tmp_file_dump="1"
		fi

		# Set umask prior writing, so we get the correct chmod just at the time of writing to disk
		umask "$(chmod_2_umask "${_file_chmod}")";

		# POSIX pipefail emulation
		exec 4>&1
		# Ignore ${_mysql_arg} to be quoted
		# shellcheck disable=SC2086
		_error_statuses="$( (
			(
				$(which mysqldump) ${_mysql_arg} "${_database}" 2> "${_tmp_file_dump}" > "${_file_name}";
				printf "%s\n%s\n" \
					"0:$?:$(head -n1 "${_tmp_file_dump}")" \
					"3:${_ern_tmp_file_dump}:${_err_tmp_file_dump}" >&3;
			)
		) 3>&1 >&4)"
		exec 4>&-

		# Delete tmp file
		if [ "${_ern_tmp_file_dump}" = "0" ]; then
			if [ ! -f "${_tmp_file_dump}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:tmp file does not exist ${_tmp_file_dump}" )"
			elif ! rm "${_tmp_file_dump}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:Could not delete tmp file ${_tmp_file_dump}" )"
			fi
		fi


	#
	# 1: Compressed
	#
	elif [ "${_dump_mode}" = "1" ]; then

		# Temp file 'mysqldump'
		_err_tmp_file_dump=""
		_ern_tmp_file_dump="0"
		if ! _tmp_file_dump="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_dump="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_dump="1"
		fi
		# Temp file 'compress'
		_err_tmp_file_comp=""
		_ern_tmp_file_comp="0"
		if ! _tmp_file_comp="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_comp="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_comp="1"
		fi

		# Set umask prior writing, so we get the correct chmod just at the time of writing to disk
		umask "$(chmod_2_umask "${_file_chmod}")";

		# POSIX pipefail emulation
		exec 4>&1
		# Ignore ${_mysql_arg} to be quoted
		# shellcheck disable=SC2086
		_error_statuses="$( (
			(
				$(which mysqldump) ${_mysql_arg} "${_database}" 2> "${_tmp_file_dump}";
				printf "%s\n%s\n" \
					"0:$?:$(head -n1 "${_tmp_file_dump}")" \
					"3:${_ern_tmp_file_dump}:${_err_tmp_file_dump}" >&3;
			) |
			(
				${_compress_bin} ${_compress_arg} 2> "${_tmp_file_comp}" > "${_file_name}";
				printf "%s\n%s\n" \
					"1:$?:$(head -n1 "${_tmp_file_comp}")" \
					"4:${_ern_tmp_file_comp}:${_err_tmp_file_comp}" >&3;
			)
		) 3>&1 >&4)"
		exec 4>&-

		# Delete tmp file
		if [ "${_ern_tmp_file_dump}" = "0" ]; then
			if [ ! -f "${_tmp_file_dump}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:tmp file does not exist ${_tmp_file_dump}" )"
			elif ! rm "${_tmp_file_dump}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:Could not delete tmp file ${_tmp_file_dump}" )"
			fi
		fi
		# Delete tmp file
		if [ "${_ern_tmp_file_comp}" = "0" ]; then
			if [ ! -f "${_tmp_file_comp}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "7:1:tmp file does not exist ${_tmp_file_comp}" )"
			elif ! rm "${_tmp_file_comp}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "7:1:Could not delete tmp file ${_tmp_file_comp}" )"
			fi
		fi


	#
	# 2: Encrypted
	#
	elif [ "${_dump_mode}" = "2" ]; then

		# Temp file 'mysqldump'
		_err_tmp_file_dump=""
		_ern_tmp_file_dump="0"
		if ! _tmp_file_dump="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_dump="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_dump="1"
		fi
		# Temp file 'encrypt'
		_err_tmp_file_encr=""
		_ern_tmp_file_encr="0"
		if ! _tmp_file_encr="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_encr="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_encr="1"
		fi

		# Set umask prior writing, so we get the correct chmod just at the time of writing to disk
		umask "$(chmod_2_umask "${_file_chmod}")";

		# POSIX pipefail emulation
		exec 4>&1

		# Ignore ${_mysql_arg} to be quoted
		# shellcheck disable=SC2086
		_error_statuses="$( (
			(
				$(which mysqldump) ${_mysql_arg} "${_database}" 2> "${_tmp_file_dump}";
				printf "%s\n%s\n" \
					"0:$?:$(head -n1 "${_tmp_file_dump}")" \
					"3:${_ern_tmp_file_dump}:${_err_tmp_file_dump}" >&3;

			) |
			(
				$(which openssl) smime -encrypt -binary -text -outform DER ${_openssl_algo} -out "${_file_name}" "${_openssl_cert}" 2> "${_tmp_file_encr}";
				printf "%s\n%s\n" \
					"2:$?:$(head -n1 "${_tmp_file_encr}")" \
					"5:${_ern_tmp_file_encr}:${_err_tmp_file_encr}" >&3;
			)

		) 3>&1 >&4)"
		exec 4>&-

		# Delete tmp file
		if [ "${_ern_tmp_file_dump}" = "0" ]; then
			if [ ! -f "${_tmp_file_dump}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:tmp file does not exist ${_tmp_file_dump}" )"
			elif ! rm "${_tmp_file_dump}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:Could not delete tmp file ${_tmp_file_dump}" )"
			fi
		fi
		# Delete tmp file
		if [ "${_ern_tmp_file_encr}" = "0" ]; then
			if [ ! -f "${_tmp_file_encr}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "8:1:tmp file does not exist ${_tmp_file_encr}" )"
			elif ! rm "${_tmp_file_encr}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "8:1:Could not delete tmp file ${_tmp_file_encr}" )"
			fi
		fi


	#
	# 3: Compressed & Encrypted
	#
	elif [ "${_dump_mode}" = "3" ]; then

		# Temp file 'mysqldump'
		_err_tmp_file_dump=""
		_ern_tmp_file_dump="0"
		if ! _tmp_file_dump="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_dump="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_dump="1"
		fi
		# Temp file 'compress'
		_err_tmp_file_comp=""
		_ern_tmp_file_comp="0"
		if ! _tmp_file_comp="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_comp="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_comp="1"
		fi
		# Temp file 'encrypt'
		_err_tmp_file_encr=""
		_ern_tmp_file_encr="0"
		if ! _tmp_file_encr="$( create_tmp_file "${_tmp_dir}/.dump_XXXXXXXXXXXXXXX" "0600" 2>/dev/null)"; then
			_err_tmp_file_encr="Could not create tmp file ${_tmp_dir}"
			_ern_tmp_file_encr="1"
		fi

		# Set umask prior writing, so we get the correct chmod just at the time of writing to disk
		umask "$(chmod_2_umask "${_file_chmod}")";

		# POSIX pipefail emulation
		exec 4>&1
		# Ignore ${_mysql_arg} to be quoted
		# shellcheck disable=SC2086
		_error_statuses="$( (
			(
				$(which mysqldump) ${_mysql_arg} "${_database}" 2> "${_tmp_file_dump}";
				printf "%s\n%s\n" \
					"0:$?:$(head -n1 "${_tmp_file_dump}")" \
					"3:${_ern_tmp_file_dump}:${_err_tmp_file_dump}" >&3;
			) |
			(
				${_compress_bin} ${_compress_arg} 2> "${_tmp_file_comp}";
				printf "%s\n%s\n" \
					"1:$?:$(head -n1 "${_tmp_file_comp}")" \
					"4:${_ern_tmp_file_comp}:${_err_tmp_file_comp}" >&3;
			) |
			(
				$(which openssl) smime -encrypt -binary -text -outform DER ${_openssl_algo} -out "${_file_name}" "${_openssl_cert}" 2> "${_tmp_file_encr}";
				printf "%s\n%s\n" \
					"2:$?:$(head -n1 "${_tmp_file_encr}")" \
					"5:${_ern_tmp_file_encr}:${_err_tmp_file_encr}" >&3;
			)

		) 3>&1 >&4)"
		exec 4>&-

		# Delete tmp file
		if [ "${_ern_tmp_file_dump}" = "0" ]; then
			if [ ! -f "${_tmp_file_dump}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:tmp file does not exist ${_tmp_file_dump}" )"
			elif ! rm "${_tmp_file_dump}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "6:1:Could not delete tmp file ${_tmp_file_dump}" )"
			fi
		fi
		# Delete tmp file
		if [ "${_ern_tmp_file_comp}" = "0" ]; then
			if [ ! -f "${_tmp_file_comp}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "7:1:tmp file does not exist ${_tmp_file_comp}" )"
			elif ! rm "${_tmp_file_comp}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "7:1:Could not delete tmp file ${_tmp_file_comp}" )"
			fi
		fi
		# Delete tmp file
		if [ "${_ern_tmp_file_encr}" = "0" ]; then
			if [ ! -f "${_tmp_file_encr}" ]; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "8:1:tmp file does not exist ${_tmp_file_encr}" )"
			elif ! rm "${_tmp_file_encr}" 2>/dev/null; then
				_error_statuses="$( printf "${_error_statuses}\n%s\n" "8:1:Could not delete tmp file ${_tmp_file_encr}" )"
			fi
		fi
	fi

	# Output the error statuses
	echo "${_error_statuses}"
	return 0
}


# Evaluate pipefail errors
#
# @param  string  Multiline pipefail string
# @output string  Multiline error messages
# @return integer Success
mysqldump_pipefail_errors() {
	_error_statuses="${1}"

	_errors=""
	_errno="0"

	IFS='
	'
	for _err in ${_error_statuses}; do
		_type="$( echo "${_err}" | awk -F ':' '{print $1}' )"
		_exit="$( echo "${_err}" | awk -F ':' '{print $2}' )"
		_msg="$(  echo "${_err}" | awk -F ':' '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"

		# ---- dumping
		if [ "${_type}" = "0" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="mysqldump: ${_msg}" || _errors="$( printf "%s\nmysqldump: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "1" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="compression: ${_msg}" || _errors="$( printf "%s\ncompression: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "2" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="encryption: ${_msg}" || _errors="$( printf "%s\nencryption: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		# ---- file creation
		elif [ "${_type}" = "3" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="tmpfile creation: ${_msg}" || _errors="$( printf "%s\ntmpfile creation: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "4" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="tmpfile creation: ${_msg}" || _errors="$( printf "%s\ntmpfile creation: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "5" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="tmpfile creation: ${_msg}" || _errors="$( printf "%s\ntmpfile creation: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		# ---- file deletion
		elif [ "${_type}" = "6" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="tmpfile deletion: ${_msg}" || _errors="$( printf "%s\ntmpfile deletion: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "7" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="tmpfile deletion: ${_msg}" || _errors="$( printf "%s\ntmpfile deletion: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "8" ]; then
			if [ "${_exit}" != "0" ]; then
				[ -z "${_errors}" ] && _errors="tmpfile deletion: ${_msg}" || _errors="$( printf "%s\ntmpfile deletion: %s" "${_errors}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		fi
	done
	unset IFS

	echo "${_errors}"
	return ${_errno}
}

# Evaluate pipefail warnings
#
# @param  string  Multiline pipefail string
# @output string  Multiline warning messages
# @return integer Success
mysqldump_pipefail_warnings() {
	_error_statuses="${1}"

	_warnings=""
	_errno="0"

	IFS='
	'
	for _err in ${_error_statuses}; do
		_type="$( echo "${_err}" | awk -F ':' '{print $1}' )"
		_exit="$( echo "${_err}" | awk -F ':' '{print $2}' )"
		_msg="$(  echo "${_err}" | awk -F ':' '{for (i=3; i<NF; i++) printf $i " "; print $NF}' )"

		# ---- dumping
		if [ "${_type}" = "0" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="mysqldump: ${_msg}" || _warnings="$( printf "%s\nmysqldump: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "1" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="compression: ${_msg}" || _warnings="$( printf "%s\ncompression: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "2" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="encryption: ${_msg}" || _warnings="$( printf "%s\nencryption: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		# ---- file creation
		elif [ "${_type}" = "3" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="tmpfile creation: ${_msg}" || _warnings="$( printf "%s\ntmpfile creation: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "4" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="tmpfile creation: ${_msg}" || _warnings="$( printf "%s\ntmpfile creation: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "5" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="tmpfile creation: ${_msg}" || _warnings="$( printf "%s\ntmpfile creation: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		# ---- file deletion
		elif [ "${_type}" = "6" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="tmpfile deletion: ${_msg}" || _warnings="$( printf "%s\ntmpfile deletion: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "7" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="tmpfile deletion: ${_msg}" || _warnings="$( printf "%s\ntmpfile deletion: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		elif [ "${_type}" = "8" ]; then
			if [ "${_exit}" = "0" ] && [ "${_msg}" != "" ]; then
				[ -z "${_warnings}" ] && _warnings="tmpfile deletion: ${_msg}" || _warnings="$( printf "%s\ntmpfile deletion: %s" "${_warnings}" "${_msg}")"
				_errno=$((_errno + 1))
			fi
		fi
	done
	unset IFS

	echo "${_warnings}"
	return ${_errno}
}



#
# Write the database dump info file with lot's of
# useful information.
#
write_info_file() {
	# File arguments
	_file_path="${1}"
	_file_name="${2}"
	_file_chmod="${3}"
	# Database arguments
	_db_name="${4}"
	_db_size="${5}"
	_db_tbl_cnt="${6}"
	_db_dump_args="${7}"
	_db_dump_time="${8}"
	# Compression arguments
	_compress_opt="${9}"
	_compress_bin="${10}"
	_compress_arg="${11}"
	# Encryption arguments
	_encrypt_opt="${12}"
	_encrypt_alg="${13}"
	_encrypt_pem="${14}"
	# MySQL arguments
	_mysql_args="${15}"
	_mysql_ssl_opt="${16}"
	_mysql_ssl_status="${17}"
	# MySQL Server Info arguments
	_mysql_server_status="${18}"
	_mysql_host="${19}"
	_mysql_port="${20}"
	_mysql_repl_type="${21}"
	_mysql_server="${22}"
	_mysql_version="${23}"


	_mtime="$( get_file_mtime "${_file_path}/${_file_name}" )"
	_mtime_h="$( formatted_timestamp "${_mtime}" "%Y-%m-%d %H:%M:%S %Z [%z]" )"

	_db_size_kb="$( div "${_db_size}" "1024" )"
	_db_size_mb="$( div "${_db_size_kb}" "1024" )"
	_db_size_mb="$( round "${_db_size_mb}" "2" )"

	#
	# Set umask prior writing.
	# (Use the same chmod as the dumps itself)
	#
	umask "$(chmod_2_umask "${_file_chmod}")";

	# Create file
	{
		echo "; ${INFO_NAME} backup record";
		echo "; Do not alter this file!";
		echo "; Creation of this file can be turned off via config file.";
		echo;
		echo "; ============================================================";
		echo "; = Local system information";
		echo "; ============================================================";
		echo "[${INFO_NAME}]";
		echo "version    = $( which ${INFO_NAME} ) (${INFO_VERSION})";
		echo "vdate      = ${INFO_DATE}";
		echo "config     = ${CONFIG_FILE}";
		echo;
		echo "[system]";
		echo "uname      = $( uname -sr )";
		echo "hostname   = $( hostname )";
		echo "user       = $( whoami )";
		echo "group      = $( id -g -n )";
		echo;
		echo "[tools]";
		echo "mysqldump  = $( which mysqldump ) $( _get_version "mysqldump" )";
		echo "mysql      = $( which mysql     ) $( _get_version "mysql" )";
		echo "compressor = $(if [ "${_compress_opt}" = "1" ]; then echo "$( which "${_compress_bin}" ) $( _get_version "${_compress_bin}" )"; else echo "Not used"; fi)";
		echo "encryptor  = $(if [ "${_encrypt_opt}"  = "1" ]; then echo "$( which openssl ) $( _get_version "openssl" )"; else echo "Not used"; fi)";
		echo;
		echo "; ============================================================";
		echo "; = Database / File information";
		echo "; ============================================================";
		echo "[database]";
		echo "db_name    = ${_db_name}";
		echo "db_size    = ${_db_size} Bytes (${_db_size_mb} MB)";
		echo "tbl_cnt    = ${_db_tbl_cnt}";
		echo;
		echo "[file]";
		echo "file_path  = ${_file_path}";
		echo "file_name  = ${_file_name}";
		echo "file_size  = $( get_file_size  "${_file_path}/${_file_name}" "b" ) Bytes ($( get_file_size "${_file_path}/${_file_name}" "m" ) MB)";
		echo "file_chmod = $( get_file_chmod "${_file_path}/${_file_name}" )";
		echo "file_owner = $( get_file_owner "${_file_path}/${_file_name}" )";
		echo "file_group = $( get_file_group "${_file_path}/${_file_name}" )";
		echo "file_mtime = ${_mtime} (${_mtime_h})";
		echo "file_md5   = $( get_file_hash "${_file_path}/${_file_name}" "md5" )";
		echo "file_sha   = $( get_file_hash "${_file_path}/${_file_name}" "sha256" )";
		echo;
		echo "; ============================================================";
		echo "; = Dump procedure information";
		echo "; ============================================================";
		echo "[mysqldump]";
		echo "encrypted  = ${_encrypt_opt}";
		echo "compressed = ${_compress_opt}";
		echo "arguments  = ${_db_dump_args}";
		echo "duration   = ${_db_dump_time} sec"
		echo;
		echo "[compression]";
		echo "compressor = $(if [ "${_compress_opt}" = "1" ]; then echo "${_compress_bin}"; fi)";
		echo "arguments  = $(if [ "${_compress_opt}" = "1" ]; then echo "${_compress_arg}"; fi)";
		echo;
		echo "[encryption]";
		echo "encryptor  = $(if [ "${_encrypt_opt}" = "1" ]; then echo "openssl"; fi)";
		echo "algorithm  = $(if [ "${_encrypt_opt}" = "1" ]; then echo "${_encrypt_alg}"; fi)";
		echo "pubkey     = $(if [ "${_encrypt_opt}" = "1" ]; then echo "${_encrypt_pem}"; fi)";
		echo;
		echo "; ============================================================";
		echo "; = Server information";
		echo "; ============================================================";
		echo "[connection]";
		echo "protocol   = $( get_mysql_connection_type_info "${_mysql_server_status}" )";
		echo "secured    = $(if [ "${_mysql_ssl_opt}" = "1" ]; then echo "SSL: ${_mysql_ssl_status}"; else echo "No SSL"; fi )";
		echo "arguments  = ${_mysql_args}";
		echo;
		echo "[server]";
		echo "hostname   = ${_mysql_host}";
		echo "port       = ${_mysql_port}";
		echo "replica    = ${_mysql_repl_type}";
		echo "version    = $(if [ "${_mysql_server}" != "" ]; then echo "${_mysql_server} ";fi)${_mysql_version}";
		echo;
	} 2>/dev/null > "${_file_path}/${_file_name}.info"

	_exit="$?"

	return ${_exit}
}



################################################################################
#
#  M I S C   F U N CT I O N S
#
################################################################################

#
# Get version information of certain binaries
#
# @param  string  binary
# @output string  Detailed version information
# @return integer Success
_get_version() {
	_tool="${1}"

	_version=""
	_extra=""
	_exit=0

	if [ "${_tool}" = "mysqldump" ]; then
		_version="$( $(which mysqldump)  --version  | sed 's/.*mysqldump[[:space:]]*Ver[[:space:]]*//' | awk -F ',' '{print $1}' )"
		_extra="$( $(which mysqldump) --version  | sed 's/.*mysqldump[[:space:]]*Ver[[:space:]]*//' | awk -F ',' '{print $2}' | sed 's/^[[:space:]]*//g' )"
	elif [ "${_tool}" = "mysql" ]; then
		_version="$( $(which mysql)  --version  | sed 's/.*mysql[[:space:]]*Ver[[:space:]]*//' | awk -F ',' '{print $1}' )"
		_extra="$( $(which mysql) --version  | sed 's/.*mysql[[:space:]]*Ver[[:space:]]*//' | awk -F ',' '{print $2}' | sed 's/^[[:space:]]*//g' )"
	elif [ "${_tool}" = "openssl" ]; then
		_version="$( $(which openssl)  version  | sed 's/^OpenSSL[[:space:]]*//' )"
		_extra=""
	elif [ "${_tool}" = "tmpwatch" ]; then
		_version="$( $(which tmpwatch) --version 2>&1 | grep -oE 'tmpwatch[[:space:]]+[0-9.]+' | sed 's/tmpwatch[[:space:]]//' )"
		_extra=""
	elif [ "${_tool}" = "tmpreaper" ]; then
		_version="$( $(which tmpreaper) --version 2>&1 | grep -oiE 'tmpreaper.*version.*[0-9.]+' | sed 's/.*Version:[[:space:]]//' )"
		_extra=""
	elif [ "${_tool}" = "gzip" ]; then
		_version="$( $(which gzip) --version 2>&1 | head -n1 )"
		_extra=""
	elif [ "${_tool}" = "bzip2" ]; then
		_version="$( $(which bzip2) --help 2>&1 | head -n1 | sed 's/.*Version[[:space:]]*//' )"
		_extra=""
	else
		_version="Unable to obtain version"
		_exit=1
	fi

	if [ "${_extra}" != "" ]; then
		echo "(${_version}) [${_extra}]"
	else
		echo "(${_version})"
	fi

	return ${_exit}
}




################################################################################
#
#  U S A G E   F U N CT I O N S
#
################################################################################



############################################################
# Usage/Help/Version
############################################################

#
# Usage
#
print_usage() {
#	printf "%s %s %s\n" "Usage:" "${INFO_NAME}" "[--conf] [--cnf] [--cron] [-v[v]] [--test] [--info] [--help] [--version]"
	printf "%s %s %s\n" "Usage:" "${INFO_NAME}" "[--conf] [--cron] [--test] [-v[v]]"
#	printf "%s %s %s\n" "      " "${INFO_NAME}" "--info"
	printf "%s %s %s\n" "      " "${INFO_NAME}" "--help"
	printf "%s %s %s\n" "      " "${INFO_NAME}" "--version"
}

#
# Display program usage
#
print_help() {

	print_usage
	echo
	echo "When invoked without any arguments, it will start dumping databases as"
	echo "defined in mysqldump-secure.conf."
	echo
	echo "  --conf          Pass a different configuration file than the default one."
	echo "                  E.g.: --conf=/etc/mysqldump-secure-alt.conf"
	echo
#	echo "  --cnf           Pass different mysql cnf credentials file than the default one."
#	echo "                  E.g.: --cnf=/etc/mysqldump-secure-slave.cnf"
#	echo
	echo "  --cron          Use for cron run. It will only output errors and warnings"
	echo "                  and will silence all info, debug and trace output."
	echo
	echo "  --test          Test requirements and exit."
	echo "                  Combine with -v or -vv for more verbose output."
	echo
	echo "  -v              Show debug output."
	echo "                  Can be combined with --conf  and --test"
	echo
	echo "  -vv             Show debug and trace output."
	echo "                  Can be combined with --conf  and --test"
	echo
#	echo "  --info          Show settings and exit."
#	echo
	echo "  --help          Show this help screen."
	echo
	echo "  --version       Show version information."
	echo
	echo
	echo "Exit codes"
	echo
	echo "  0               All fine, no fatals, no errors and no warnings occured."
	echo "  1               Warnings occured, but all dumps were successfull."
	echo "  2               Errors occured, but all dumps were successfull."
	echo "  3               Failed. Mysqldump encountered errors."
	echo "  4               Abort. The program aborted, due to missing requirements,"
	echo "                  wrong arguments or a misconfiguration."
	echo
	echo "Further reading"
	echo
	echo "See 'man mysqldump-secure' for more help."
	echo "Visist http://mysqldump-secure.org and browse documentation."
}
# TODO:
# --------
# --info			Shows current configuration and paths
# --cli				Start interactive cli (mysqldump-secure-cli) to show dumps, import dumps, set configs, etc



#
# Display program version and credits
#
print_version() {
	printf "Name:    %s\n" "${INFO_NAME}"
	printf "Version: %s (%s)\n" "${INFO_VERSION}" "${INFO_DATE}"
	printf "Author:  %s (%s)\n" "${INFO_AUTHOR}" "${INFO_GPGKEY}"
	printf "Code:    %s\n" "${INFO_REPO}"
	printf "URL:     %s\n" "${INFO_URL}"
	printf "License: %s\n" "${INFO_LICENSE}"
}









####################################################################################################
####################################################################################################
##
##  M A I N   E N T R Y   P O I N T  ( A R G U M E N T S )
##
####################################################################################################
####################################################################################################



################################################################################
#
# COMMAND LINE ARGUMENTS
#
################################################################################

############################################################
# 1.) Evaluate cmd arguments
############################################################


_ARG_CONF="0"	# Different config file specified?
_ARG_VERB="1"	# Verbose mode?
_ARG_CRON="0"	# Cron mode?
_ARG_TEST="0"	# Test mode?
_ARG_HELP="0"	# Show help?
_ARG_VERS="0"	# Show version?

_CUS_CONF=""	# Where is the custom config?
#_ARG_INFO="0"


#
# Loop over command line arguments
#
while [ $# -gt 0  ]; do
	case "$1" in

		--conf=*)
			if [ "${_ARG_HELP}" = "1" ] || [ "${_ARG_VERS}" = "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_CONF}" != "0" ]; then
				echo "Invalid. Argument '--conf' already specified." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_CONF="1"
			_CUS_CONF="$(echo "$1" | sed 's/--conf=//g')"
			;;


		-v)
			if [ "${_ARG_HELP}" = "1" ] || [ "${_ARG_VERS}" = "1" ] || [ "${_ARG_CRON}" = "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_VERB}" != "1" ]; then
				echo "Invalid. Argument '-v' already specified (use -vv for more verbosity)." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_VERB="2"
			;;


		-vv)
			if [ "${_ARG_HELP}" = "1" ] || [ "${_ARG_VERS}" = "1" ] || [ "${_ARG_CRON}" = "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_VERB}" != "1" ]; then
				echo "Invalid. Argument '-v' already specified (use -vv for more verbosity)." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_VERB="3"
			;;


		--cron)
			if [ "${_ARG_TEST}" = "1" ] || [ "${_ARG_HELP}" = "1" ] || [ "${_ARG_VERS}" = "1" ] || [ "${_ARG_VERB}" != "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_CRON}" != "0" ]; then
				echo "Invalid. Argument '--cron' already specified." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_CRON="1"
			;;


		--test)
			if [ "${_ARG_CRON}" = "1" ] || [ "${_ARG_HELP}" = "1" ] || [ "${_ARG_VERS}" = "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_TEST}" != "0" ]; then
				echo "Invalid. Argument '--test' already specified." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_TEST="1"
			;;

#		--info)
#			_ARG_INFO="1"
#			;;


		--help)
			if [ "${_ARG_CONF}" = "1" ] || [ "${_ARG_CRON}" = "1" ] || [ "${_ARG_TEST}" = "1" ] || [ "${_ARG_VERS}" = "1" ] || [ "${_ARG_VERB}" != "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_HELP}" != "0" ]; then
				echo "Invalid. Argument '--help' already specified." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_HELP="1"
			print_help
			exit 0
			;;


		--version)
			if [ "${_ARG_CONF}" = "1" ] || [ "${_ARG_CRON}" = "1" ] || [ "${_ARG_TEST}" = "1" ] || [ "${_ARG_HELP}" = "1" ] || [ "${_ARG_VERB}" != "1" ]; then
				echo "Invalid. Arguments cannot be mixed." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			if [ "${_ARG_VERS}" != "0" ]; then
				echo "Invalid. Argument '--versopm' already specified." 1>&2
				echo "Type '${INFO_NAME} --help' for available options." 1>&2
				exit $EXIT_ABORT
			fi
			_ARG_VERS="1"
			print_version
			exit 0
			;;


		*)
			echo "Invalid argument: '${1}'." 1>&2
			echo "Type '${INFO_NAME} --help' for available options." 1>&2
			exit $EXIT_ABORT
			;;

	esac
	shift
done


#
# Set output verbosity
#
if [ "${_ARG_CRON}" = "1" ]; then
	OUT_VERBOSITY="0" # only show fatals, errors and warnings
else
	OUT_VERBOSITY="${_ARG_VERB}"	# 1, 2 or 3
fi


#
# Set file verbosity (default is off)
#
LOG_VERBOSITY="0"


#
# Use default config or user specified config file
#
if [ -z "${_CUS_CONF-}" ]; then
	CONFIG_FILE="${_DEFAULT_CONFIG_FILE}"
else
	CONFIG_FILE="${_CUS_CONF}"
fi


############################################################
# 2.) Validate config file location/rights
############################################################

if [ ! -f "${CONFIG_FILE}" ]; then
	debug "fatal" "(RUN): Configuration file not found in ${CONFIG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	exit $EXIT_ABORT
fi
if [ -L "${CONFIG_FILE}" ]; then
	if _TMP="$(get_file_realpath "${CONFIG_FILE}")" >/dev/null 2>&1; then
		debug "trace" "(RUN): \$CONFIG_FILE is a symlink. Setting target to: ${_TMP}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		CONFIG_FILE="${_TMP}"
	else
		debug "fatal" "(RUN): Cannot resolve symlink of: $CONFIG_FILE" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		exit $EXIT_ABORT
	fi
fi
if [ ! -r "${CONFIG_FILE}" ]; then
	debug "fatal" "(RUN): Configuration file is not readable in ${CONFIG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	exit $EXIT_ABORT
fi
if ! compare_chmod "$(get_file_chmod "${CONFIG_FILE}")" "${_DEFAULT_CONFIG_CHMOD}" > /dev/null 2>&1 ; then
	debug    "warn" "(RUN): Configuration file ${CONFIG_FILE} has dangerous permissions: $(get_file_chmod "${CONFIG_FILE}"), should be: ${_DEFAULT_CONFIG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	debug_pi "warn" "(RUN): Trying to chmod to ${_DEFAULT_CONFIG_CHMOD}..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

	if ! chmod ${_DEFAULT_CONFIG_CHMOD} "${CONFIG_FILE}" > /dev/null 2>&1 ; then
		debug_i "fatal" "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug   "fatal" "Failed to chmod ${CONFIG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug   "fatal" "Fix it manually to ${_DEFAULT_CONFIG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug   "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		exit $EXIT_ABORT
	else
		debug_i "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	fi
fi



# Read config file
# Comment required by shellcheck,
# See problem here: https://github.com/koalaman/shellcheck/wiki/SC1090
# shellcheck disable=SC1090
#
# TODO: Manually parse the file via sed or awk to be safer
. "${CONFIG_FILE}"







####################################################################################################
####################################################################################################
##
##  M A I N   E N T R Y   P O I N T   ( E R R O R   C H E C K I N G )
##
####################################################################################################
####################################################################################################




################################################################################
#
# EVALUATE CONFIG FILE
#
################################################################################


############################################################
# Logging Options
############################################################

# Be really strict on checking if we are going to log to file
# or not. Also make sure that the logfile is writeable and
# that no other has read permissions to the file.
if ! set | grep '^LOG=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$LOG variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	debug "warn" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	LOG=0
	LOG_FILE=""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${LOG}" ]; then
	debug "warn" "(CFG): \$LOG variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	debug "warn" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	LOG=0
	LOG_FILE=""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${LOG}" = "1" ] || [ "${LOG}" = "2" ] || [ "${LOG}" = "3" ]; then

	# Check chmod variable
	if ! set | grep '^LOG_CHMOD=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$LOG_CHMOD variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug "warn" "(CFG): Setting default to: ${_DEFAULT_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		LOG_CHMOD="${_DEFAULT_LOG_CHMOD}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${LOG_CHMOD}" ]; then
		debug "warn" "(CFG): \$LOG_CHMOD variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug "warn" "(CFG): Setting default to: ${_DEFAULT_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		LOG_CHMOD="${_DEFAULT_LOG_CHMOD}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif ! valid_chmod "${LOG_CHMOD}" > /dev/null 2>&1; then
		debug "warn" "(CFG): Invalid value for \$LOG_CHMOD: ${LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug "warn" "(CFG): Setting default to: ${_DEFAULT_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		LOG_CHMOD="${_DEFAULT_LOG_CHMOD}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	fi

	# Check logfile variable
	if ! set | grep '^LOG_FILE=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$LOG_FILE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug "warn" "(OPT): Setting default to: ${_DEFAULT_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		LOG_FILE="${_DEFAULT_LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${LOG_FILE}" ]; then
		debug "warn" "(CFG): \$LOG_FILE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		debug "warn" "(OPT): Setting default to: ${_DEFAULT_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
		LOG_FILE="${_DEFAULT_LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	fi

	# Resolve symlink?
	if [ -L "${LOG_FILE}" ]; then
		if _TMP="$(get_file_realpath "${LOG_FILE}")" >/dev/null 2>&1; then
			debug "trace" "(RUN): \$LOG_FILE is a symlink. Setting target to: ${_TMP}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			LOG_FILE="${_TMP}"
		else
			debug "err" "(RUN): Cannot resolve symlink of: $LOG_FILE" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			debug "err" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			LOG=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		fi
	fi

	# Check if logfile exists
	if [ ! -f "${LOG_FILE}" ]; then
		# Check if logging directory exists
		if [ ! -d "$(dirname "${LOG_FILE}")" ]; then
			debug    "warn" "(RUN): Log directory does not exist in $(dirname "${LOG_FILE}")" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			debug_pi "warn" "(RUN): Trying to create..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
			if ! mkdir -p "$(dirname "${LOG_FILE}")" > /dev/null 2>&1 ; then
				debug_i "err" "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				debug   "err" "(RUN): Failed to create directoy: $(dirname "${LOG_FILE}")" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				debug   "err" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				LOG=0
				MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
			else
				debug_i "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			fi
		fi
		# If logging is still OK (dir exists), check if the file can be created
		if [ "${LOG}" != "0" ]; then
			debug    "warn" "(RUN): Log file does not exist in ${LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			debug_pi "warn" "(RUN): Trying to create..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

			if ! touch "${LOG_FILE}" > /dev/null 2>&1 ; then
				debug_i "err"  "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				debug   "err"  "(RUN): Failed to create file: ${LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				debug   "err"  "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				LOG=0
				MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
			else
				debug_i "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			fi
		fi
	fi

	# Logging still on?
	# Check for permissions
	if [ "${LOG}" != "0" ]; then
		if ! compare_chmod "$(get_file_chmod "${LOG_FILE}")" "${LOG_CHMOD}" > /dev/null 2>&1 ; then
			debug    "warn" "(RUN): Log file has wrong permissions: $(get_file_chmod "${LOG_FILE}"), should be: ${LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			debug_pi "warn" "(RUN): Trying to chmod to ${LOG_CHMOD}..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

			if ! chmod ${LOG_CHMOD} "${LOG_FILE}" > /dev/null 2>&1 ; then
				debug_i "err"  "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				debug   "err"  "(RUN): Failed to chmod ${LOG_CHMOD} ${LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				debug   "err"  "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
				LOG=0
				MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
			else
				debug_i "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			fi
		fi
	fi

	# Logging still on?
	# Check if it is writeable
	if [ "${LOG}" != "0" ]; then

		# Logfile not writeable
		if [ ! -w "${LOG_FILE}" ]; then
			debug  "err" "(RUN): Log file ${LOG_FILE} not writeable. Check \$LOG_CHMOD value inf config." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			debug  "err" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
			LOG=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		fi
	fi
elif [ "${LOG}" = "0" ]; then
	debug "info" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	LOG_FILE=""
else
	debug "warn" "(CFG): Invalid value for \$LOG: ${LOG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	debug "warn" "(OPT): Logging disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" ""
	LOG=0
	LOG_FILE=""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# Is logging still enabled after all checks?
# Write to the file
if [ "${LOG}" != "0" ]; then

	# Assign new value for logging verbosity
	LOG_VERBOSITY="${LOG}"

	{
		echo;
		echo "--------------------------------------------------------------------------------";
		echo "$(date '+%Y-%m-%d') $(date '+%H:%M:%S') Starting";
	} >> "${LOG_FILE}"
	debug "debug" "(CFG): Using config: ${CONFIG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "info"  "(OPT): Logging enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "debug" "(OPT): Log level: ${LOG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "debug" "(OPT): Log file: ${LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "debug" "(CFG): Using config: ${CONFIG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi





############################################################
# Command line arguments
############################################################
if [ "${_ARG_CRON}" = "1" ]; then
	# Note: This will never be outputted to stdout/stderr (only to logfile)
	debug "trace" "(ARG): Running in cron mode (--cron)." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi
if [ "${_ARG_TEST}" = "1" ]; then
	debug "info" "(ARG): Running in test mode (--test)." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi
if [ "${_ARG_VERB}" = "1" ]; then
	debug "trace" "(ARG): Running in verbose mode (--verbose)." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi
#if [ "${_ARG_INFO}" = "1" ]; then
#	debug "info" "Showing information only (--info)."  "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
#fi



############################################################
# Tmp folder
############################################################


# Check if mktemp exits
if ! command -v mktemp > /dev/null 2>&1 ; then
	debug "fatal" "(RUN): 'mktemp' not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi

# Check $TMP_DIR variable
if ! set | grep '^TMP_DIR=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$TMP_DIR variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	TMP_DIR=${_DEFAULT_TMP_DIR}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${TMP_DIR}" ]; then
	debug "warn" "(CFG): \$TMP_DIR variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	TMP_DIR=${_DEFAULT_TMP_DIR}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# Check directory
if [ ! -d "${TMP_DIR}" ]; then
	debug "fatal" "(CFG): \$TMP_DIR is not a directory: ${TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): Change value of \$TMP_DIR in your config" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ ! -w "${TMP_DIR}" ]; then
	debug "fatal" "(CFG): \$TMP_DIR is not writeable" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): Change value of \$TMP_DIR in your config" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi
debug "trace" "(CFG): Using temporary directory: ${TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"




############################################################
# Destination Directory and Prefix
############################################################

# Check $DUMP_DIR_CHMOD
if ! set | grep '^DUMP_DIR_CHMOD=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$DUMP_DIR_CHMOD variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_DIR_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_DIR_CHMOD=${_DEFAULT_DUMP_DIR_CHMOD}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${DUMP_DIR_CHMOD}" ]; then
	debug "warn" "(CFG): \$DUMP_DIR_CHMOD variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_DIR_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_DIR_CHMOD=${_DEFAULT_DUMP_DIR_CHMOD}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif ! valid_chmod "${DUMP_DIR_CHMOD}" > /dev/null 2>&1; then
	debug "warn" "(CFG): Invalid value for \$DUMP_DIR_CHMOD: ${DUMP_DIR_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_DIR_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_DIR_CHMOD="${_DEFAULT_DUMP_DIR_CHMOD}"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# Check $DUMP_FILE_CHMOD
if ! set | grep '^DUMP_FILE_CHMOD=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$DUMP_FILE_CHMOD variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_FILE_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_CHMOD=${_DEFAULT_DUMP_DIR_CHMOD}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${DUMP_FILE_CHMOD}" ]; then
	debug "warn" "(CFG): \$DUMP_FILE_CHMOD variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_FILE_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_CHMOD=${_DEFAULT_DUMP_DIR_CHMOD}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif ! valid_chmod "${DUMP_FILE_CHMOD}" > /dev/null 2>&1; then
	debug "warn" "(CFG): Invalid value for \$DUMP_FILE_CHMOD: ${DUMP_FILE_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_FILE_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_CHMOD="${_DEFAULT_DUMP_DIR_CHMOD}"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# Check $DUMP_DIR
if ! set | grep '^DUMP_DIR=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$DUMP_DIR variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_DIR=${_DEFAULT_DUMP_DIR}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${DUMP_DIR}" ]; then
	debug "warn" "(CFG): \$DUMP_DIR variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_DIR=${_DEFAULT_DUMP_DIR}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# Resolve symlink?
if [ -L "${DUMP_DIR}" ]; then
	if _TMP="$(get_file_realpath "${DUMP_DIR}")" >/dev/null 2>&1; then
		debug "trace" "(RUN): \$DUMP_DIR is a symlink. Setting target to: ${_TMP}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		DUMP_DIR="${_TMP}"
	else
		debug "fatal" "(RUN): Cannot resolve symlink of: $DUMP_DIR" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi
fi

if [ ! -d "${DUMP_DIR}" ]; then
	debug    "warn" "(RUN): Destination dir does not exist: ${DUMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug_pi "warn" "(RUN): Trying to create... " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

	if ! mkdir -p "${DUMP_DIR}" > /dev/null 2>&1 ; then
		debug_i "fatal"  "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug   "fatal"  "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	else
		debug_i  "warn" "Done\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug_pi "warn" "(RUN): Trying to chmod to: ${DUMP_DIR_CHMOD}..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		if ! chmod ${DUMP_DIR_CHMOD} "${DUMP_DIR}" >/dev/null 2>&1; then
			debug_i "fatal"  "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "fatal"  "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			exit $EXIT_ABORT
		else
			debug_i "warn" "Done\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi
	fi
fi

# Check correct permissions of destination dir
if ! compare_chmod "$(get_file_chmod "${DUMP_DIR}")" "${DUMP_DIR_CHMOD}" > /dev/null 2>&1 ; then
	debug    "warn" "(RUN): Destination dir has wrong permissions: $(get_file_chmod "${DUMP_DIR}"), but should ${DUMP_DIR_CHMOD}." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug_pi "warn" "(RUN): Trying to chmod to ${DUMP_DIR_CHMOD}... " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

	if ! chmod "${DUMP_DIR_CHMOD}" "${DUMP_DIR}" > /dev/null 2>&1 ; then
		debug_i "fatal" "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug   "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	else
		debug_i "warn" "Done\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	fi
fi
# Check if destination dir is writeable
if [ ! -w "${DUMP_DIR}" ]; then
	debug "fatal" "(RUN): Destination dir ${DUMP_DIR} is not writeable" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): Fix your configured permissions" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi

# Show information
debug "debug" "(CFG): Destination dir: ${DUMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


# Check $DUMP_FILE_PRE
if ! set | grep '^DUMP_FILE_PRE=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$DUMP_FILE_PRE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_FILE_PRE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_PRE=${_DEFAULT_DUMP_FILE_PRE}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${DUMP_FILE_PRE}" ]; then
	debug "warn" "(CFG): \$DUMP_FILE_PRE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_DUMP_FILE_PRE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_PRE=${_DEFAULT_DUMP_FILE_PRE}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# Show information
debug "debug" "(CFG): Using file Prefix: ${DUMP_FILE_PRE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"



############################################################
# MySQL
############################################################

# Check Binaries
if ! command -v mysql > /dev/null 2>&1 ; then
	debug "fatal" "(RUN): 'mysql' not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi
if ! command -v mysqldump > /dev/null 2>&1 ; then
	debug "fatal" "(RUN): 'mysqldump' not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi

# Check $MYSQL_CNF_FILE
if ! set | grep '^MYSQL_CNF_FILE=' >/dev/null 2>&1; then
	debug "fatal" "(CFG): \$MYSQL_CNF_FILE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ -z "${MYSQL_CNF_FILE}" ]; then
	debug "fatal" "(CFG): \$MYSQL_CNF_FILE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi

# Resolve symlink?
if [ -L "${MYSQL_CNF_FILE}" ]; then
	if _TMP="$(get_file_realpath "${MYSQL_CNF_FILE}")" >/dev/null 2>&1; then
		debug "trace" "(RUN): \$MYSQL_CNF_FILE is a symlink. Setting target to: ${_TMP}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MYSQL_CNF_FILE="${_TMP}"
	else
		debug "fatal" "(RUN): Cannot resolve symlink of: $MYSQL_CNF_FILE" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi
fi

# Check access and chmod
if [ ! -f "${MYSQL_CNF_FILE}" ]; then
	debug "fatal" "(RUN): MySQL CNF file not found: ${MYSQL_CNF_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ ! -r "${MYSQL_CNF_FILE}" ]; then
	debug "fatal" "(RUN): MySQL CNF file is not readable: ${MYSQL_CNF_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif ! compare_chmod "$(get_file_chmod "${MYSQL_CNF_FILE}")" "${_DEFAULT_MYSQL_CNF_CHMOD}" > /dev/null 2>&1 ; then
	debug "fatal" "(RUN): MySQL CNF file has dangerous permissions: $(get_file_chmod "${MYSQL_CNF_FILE}")." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): Fix it to ${_DEFAULT_MYSQL_CNF_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): If you are not alone on the machine, the password inside could have been compromised by now." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): If so, change your database password." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi

# Validate contents
if ! _MSG="$(validate_cnf_file "${MYSQL_CNF_FILE}")"; then
	debug "fatal" "(RUN): Invalid cnf file." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): ${_MSG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi



# Check MYSQL_SSL_ENABLE
if ! set | grep '^MYSQL_SSL_ENABLE=' >/dev/null 2>&1; then
	debug "fatal" "(CFG): \$MYSQL_SSL_ENABLE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): It is not clear whether or not you only allow secure connections." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ -z "${MYSQL_SSL_ENABLE}" ]; then
	debug "fatal" "(CFG): \$MYSQL_SSL_ENABLE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): It is not clear whether or not you only allow secure connections." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ "${MYSQL_SSL_ENABLE}" = "1" ]; then
	if ! set | grep '^MYSQL_SSL_CA_PEM=' >/dev/null 2>&1; then
		debug "fatal" "(CFG): \$MYSQL_SSL_CA_PEM variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "(HLP): It is required for SSL connections" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal"  "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	elif [ -z "${MYSQL_SSL_CA_PEM}" ]; then
		debug "fatal" "(CFG): \$MYSQL_SSL_CA_PEM variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "(HLP): It is required for SSL connections" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	elif [ ! -f "${MYSQL_SSL_CA_PEM}" ]; then
		debug "fatal" "(RUN): \$MYSQL_SSL_CA_PEM=\"$MYSQL_SSL_CA_PEM\" does not exist" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "(HLP): It is required for SSL connections" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi

	MYSQL_SSL_ARGS="--ssl-ca=${MYSQL_SSL_CA_PEM}"

	# MYSQL_SSL_CLIENT_CERT_PEM
	if [ -n "${MYSQL_SSL_CLIENT_CERT_PEM-}" ]; then
		if [ ! -f "${MYSQL_SSL_CLIENT_CERT_PEM}" ]; then
			debug "fatal" "(RUN): \$MYSQL_SSL_CLIENT_CERT_PEM=\"$MYSQL_SSL_CLIENT_CERT_PEM\" does not exist" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "(HLP): Comment it out or specify the correct path." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			exit $EXIT_ABORT
		fi
		MYSQL_SSL_ARGS="${MYSQL_SSL_ARGS} --ssl-cert=${MYSQL_SSL_CLIENT_CERT_PEM}"
	fi
	# MYSQL_SSL_CLIENT_KEY_PEM
	if [ -n "${MYSQL_SSL_CLIENT_KEY_PEM-}" ]; then
		if [ ! -f "${MYSQL_SSL_CLIENT_KEY_PEM}" ]; then
			debug "fatal" "(RUN): \$MYSQL_SSL_CLIENT_KEY_PEM=\"$MYSQL_SSL_CLIENT_KEY_PEM\" does not exist" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "(HLP): Comment it out or specify the correct path." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			exit $EXIT_ABORT
		fi
		MYSQL_SSL_ARGS="${MYSQL_SSL_ARGS} --ssl-key=${MYSQL_SSL_CLIENT_KEY_PEM}"
	fi

	debug "info"  "(OPT): MySQL SSL connection enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "debug" "(OPT): MySQL SSL arguments: ${MYSQL_SSL_ARGS}." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
elif [ "${MYSQL_SSL_ENABLE}" = "0" ]; then
	MYSQL_SSL_ENABLE=0
	MYSQL_SSL_ARGS=""
	debug "info" "(OPT): MySQL SSL connection disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "fatal" "(CFG): Wrong value for \$MYSQL_SSL_ENABLE: $MYSQL_SSL_ENABLE" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): It is not clear whether or not you only allow secure connections." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi




############################################################
# MySQL Dump Opts
############################################################

# MYSQL_OPTS
if ! set | grep '^MYSQL_OPTS=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$MYSQL_OPTS variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(RUN): Setting default to no custom mysqldump options" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MYSQL_OPTS=""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${MYSQL_SSL_ENABLE}" ]; then
	debug "trace" "(CFG): \$MYSQL_OPTS is empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MYSQL_OPTS=""
fi

debug "trace"  "(CFG): mysqldump custom options: ${MYSQL_OPTS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


# MYSQL_OPTS_QUICK_MIN_SIZE
if ! set | grep '^MYSQL_OPTS_QUICK_MIN_SIZE=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$MYSQL_OPTS_QUICK_MIN_SIZE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MYSQL_OPTS_QUICK_MIN_SIZE=${_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${MYSQL_OPTS_QUICK_MIN_SIZE}" ]; then
	debug "warn" "(CFG): \$MYSQL_OPTS_QUICK_MIN_SIZE is empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MYSQL_OPTS_QUICK_MIN_SIZE=${_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif ! isint "${MYSQL_OPTS_QUICK_MIN_SIZE}" >/dev/null 2>&1; then
	debug "warn" "(CFG): Wrong integer value for \$MYSQL_OPTS_QUICK_MIN_SIZE: ${MYSQL_OPTS_QUICK_MIN_SIZE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(CFG): Setting default to: ${_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MYSQL_OPTS_QUICK_MIN_SIZE=${_DEFAULT_MYSQL_OPTS_QUICK_MIN_SIZE}
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi
debug "trace"  "(CFG): mysqldump '--quick':      Applied to DB's >= ${MYSQL_OPTS_QUICK_MIN_SIZE} MB" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
debug "trace"  "(CFG): mysqldump '--skip-quick': Applied to DB's  < ${MYSQL_OPTS_QUICK_MIN_SIZE} MB" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


############################################################
# Bad MySQL Opts
############################################################
for opt in ${MYSQL_OPTS}; do
	for evil in ${MDS_MYSQL_EVIL_OPTS}; do
		if echo "${opt}" | grep -e "^${evil}" > /dev/null 2>&1; then
			debug "fatal" "(CFG): Insecure mysqldump option found in MYSQL_OPTS: '${opt}'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "(RUN): Blacklist pattern: '^${evil}'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "(HLP): Remove it. This option is handled by the tool itself." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			exit $EXIT_ABORT
		fi
	done
	for bad in ${MDS_MYSQL_BAD_OPTS}; do
		if echo "${opt}" | grep -e "^${bad}" > /dev/null 2>&1; then
			debug "fatal" "(CFG): Disallowed mysqldump option found in MYSQL_OPTS: '${opt}'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "(RUN): Blacklist pattern: '^${bad}'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "(HLP): Remove it. This option can be controlled via the configuration file itself." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			exit $EXIT_ABORT
		fi
	done
done





############################################################
# Consistency/Transactions
############################################################

# CONSISTENT_DUMP_ONLY_INNODB
if ! set | grep '^CONSISTENT_DUMP_ONLY_INNODB=' >/dev/null 2>&1; then
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_ONLY_INNODB variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--single-transaction')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_ONLY_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${CONSISTENT_DUMP_ONLY_INNODB}" ]; then
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_ONLY_INNODB variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--single-transaction')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_ONLY_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${CONSISTENT_DUMP_ONLY_INNODB}" = "1" ]; then
	debug "trace" "(CFG): InnoDB-only:  Dumping consistently across tables (using '--single-transaction')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
elif [ "${CONSISTENT_DUMP_ONLY_INNODB}" = "0" ]; then
	debug "trace" "(CFG): InnoDB-only:  Not dumping consistently across tables (using '--skip-lock-tables') [unsafe]" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_ONLY_INNODB variable has an invalid value: ${CONSISTENT_DUMP_ONLY_INNODB}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--single-transaction')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_ONLY_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# CONSISTENT_DUMP_NO_INNODB
if ! set | grep '^CONSISTENT_DUMP_NO_INNODB=' >/dev/null 2>&1; then
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_NO_INNODB variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_NO_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${CONSISTENT_DUMP_NO_INNODB}" ]; then
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_NO_INNODB variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_NO_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${CONSISTENT_DUMP_NO_INNODB}" = "1" ]; then
	debug "trace" "(CFG): InnoDB-none:  Dumping consistently across tables (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
elif [ "${CONSISTENT_DUMP_NO_INNODB}" = "0" ]; then
	debug "trace" "(CFG): InnoDB-none:  Not dumping consistently across tables (using '--skip-lock-tables') [unsafe]" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_NO_INNODB variable has an invalid value: ${CONSISTENT_DUMP_NO_INNODB}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_NO_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi

# CONSISTENT_DUMP_MIXED_INNODB
if ! set | grep '^CONSISTENT_DUMP_MIXED_INNODB=' >/dev/null 2>&1; then
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_MIXED_INNODB variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_MIXED_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${CONSISTENT_DUMP_MIXED_INNODB}" ]; then
	debug "warn" "(CFG): \$CONSISTENT_DUMP_MIXED_INNODB variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_MIXED_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${CONSISTENT_DUMP_MIXED_INNODB}" = "1" ]; then
	debug "trace" "(CFG): InnoDB-mixed: Dumping consistently across tables (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
elif [ "${CONSISTENT_DUMP_MIXED_INNODB}" = "2" ]; then
	debug "trace" "(CFG): InnoDB-mixed: Dumping consistently across tables (using '--single-transaction') [unsafe]" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
elif [ "${CONSISTENT_DUMP_MIXED_INNODB}" = "0" ]; then
	debug "trace" "(CFG): InnoDB-mixed: Not dumping consistently across tables (using '--skip-lock-tables') [unsafe]" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "warn"  "(CFG): \$CONSISTENT_DUMP_MIXED_INNODB variable has an invalid value: ${CONSISTENT_DUMP_MIXED_INNODB}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn"  "(RUN): Setting default to: 1 (using '--lock-tables')" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	CONSISTENT_DUMP_MIXED_INNODB="1"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi




############################################################
# IGNORE/REQUIRE
############################################################

# Ignored databases
if ! set | grep '^IGNORE=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$IGNORE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(HLP): Define \$IGNORE in your config to get rid of this warning" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(RUN): No database will be ignored" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	IGNORE=""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${IGNORE}" ]; then
	debug "trace" "(CFG): \$IGNORE variable is empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "trace" "(RUN): No database will be ignored" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	IGNORE=""
else
	debug "trace" "(CFG): Ignored DB's: ${IGNORE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi

# Required databases
if ! set | grep '^REQUIRE=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$REQUIRE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(HLP): Define \$REQUIRE in your config to get rid of this warning" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(RUN): No database will be required" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	REQUIRE=""
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${REQUIRE}" ]; then
	debug "trace" "(CFG): \$REQUIRE variable is empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "trace" "(RUN): No database will explicitly be required" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	REQUIRE=""
else
	debug "trace" "(CFG): Required DB's: ${REQUIRE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi





############################################################
# Compression
############################################################
# Check $COMPRESS
if ! set | grep '^COMPRESS=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$COMPRESS variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	COMPRESS=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${COMPRESS}" ]; then
	debug "warn" "(CFG): \$COMPRESS variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	COMPRESS=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${COMPRESS}" = "1" ]; then

	# COMPRESS_BIN
	if ! set | grep '^COMPRESS_BIN=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$COMPRESS_BIN variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		COMPRESS=0
		COMPRESS_BIN=""
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${COMPRESS_BIN}" ]; then
		debug "warn" "(CFG): \$COMPRESS_BIN variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		COMPRESS=0
		COMPRESS_BIN=""
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	fi

	# If compression is still enabled
	if [ "${COMPRESS}" = "1" ]; then
		# Does COMPRESS_BIN binary exist?
		if ! command -v "${COMPRESS_BIN}" > /dev/null 2>&1 ; then
			debug "err" "(CFG): ${COMPRESS_BIN} not found. Fix \$COMPRESS_BIN" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "err" "(HLP): Fix \$COMPRESS_BIN" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "err" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			COMPRESS=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		fi
	fi

	# If compression is still enabled
	if [ "${COMPRESS}" = "1" ]; then
		# COMPRESS_ARG
		if ! set | grep '^COMPRESS_ARG=' >/dev/null 2>&1; then
			debug "warn" "(CFG): \$COMPRESS_ARG variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(RUN): Using no command line arguments for ${COMPRESS_BIN}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			COMPRESS_ARG=""
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		# Can be empty
		elif [ -z "${COMPRESS_ARG}" ]; then
			debug "trace" "(CFG): No compression arguments specified for ${COMPRESS_BIN}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi
	fi

	# If compression is still enabled
	if [ "${COMPRESS}" = "1" ]; then
		# COMPRESS_ARG
		if ! set | grep '^COMPRESS_EXT=' >/dev/null 2>&1; then
			debug "warn" "(CFG): \$COMPRESS_EXT variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(RUN): Using 'compressed' as file extension." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			COMPRESS_EXT="compressed"
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		elif [ -z "${COMPRESS_EXT}" ]; then
			debug "warn" "(CFG): \$COMPRESS_EXT variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(RUN): Using 'compressed' as file extension." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			COMPRESS_EXT="compressed"
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		fi
	fi

	# If compression is still enabled
	if [ "${COMPRESS}" = "1" ]; then
		debug "info"  "(OPT): Compression enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "debug" "(OPT): Compression arguments: ${COMPRESS_BIN} ${COMPRESS_ARG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	fi

elif [ "${COMPRESS}" = "0" ]; then
	debug "info" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "warn" "(CFG): Invalid value for \$COMPRESS: ${COMPRESS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Compression disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	COMPRESS=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi



############################################################
# Encryption
############################################################
if ! set | grep '^ENCRYPT=' >/dev/null 2>&1; then
	debug "fatal" "(CFG): \$ENCRYPT variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): It is not clear whether or not you only allow encrypted dumps." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ -z "${ENCRYPT}" ]; then
	debug "fatal" "(CFG): \$ENCRYPT variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): It is not clear whether or not you only allow encrypted dumps." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
elif [ "${ENCRYPT}" = "1" ]; then

	# OPENSSL_PUBKEY_PEM
	if ! set | grep '^OPENSSL_PUBKEY_PEM=' >/dev/null 2>&1; then
		debug "fatal" "(CFG): \$OPENSSL_PUBKEY_PEM variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	elif [ -z "${OPENSSL_PUBKEY_PEM}" ]; then
		debug "fatal" "(CFG): \$OPENSSL_PUBKEY_PEM variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	elif [ ! -f "${OPENSSL_PUBKEY_PEM}" ]; then
		debug "fatal"  "(RUN): OpenSSL pubkey not found in ${OPENSSL_PUBKEY_PEM}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal"  "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	elif [ ! -r "${OPENSSL_PUBKEY_PEM}" ]; then
		debug "fatal" "(RUN): OpenSSL pubkey not readable" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi

	# OpenSSL Algorithm argument
	if ! set | grep '^OPENSSL_ALGO_ARG=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$OPENSSL_ALGO_ARG variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(RUN): Encryption defaults to: ${_DEFAULT_OPENSSL_ALGO_ARG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		OPENSSL_ALGO_ARG="${_DEFAULT_OPENSSL_ALGO_ARG}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${OPENSSL_ALGO_ARG}" ]; then
		debug "warn" "(CFG): \$OPENSSL_ALGO_ARG variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(RUN): Encryption defaults to: ${_DEFAULT_OPENSSL_ALGO_ARG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		OPENSSL_ALGO_ARG="${_DEFAULT_OPENSSL_ALGO_ARG}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	fi

	# Test OpenSSL
	if ! command -v openssl > /dev/null 2>&1 ; then
		debug "fatal" "(RUN): 'openssl' not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi
	if ! echo "test" | $(which openssl) smime -encrypt -binary -text -outform DER ${OPENSSL_ALGO_ARG} "${OPENSSL_PUBKEY_PEM}" > /dev/null 2>&1 ; then
		debug "fatal" "(RUN): openssl encryption test failed. Validate \$OPENSSL_ALGO_ARG" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi

	# ENABLE_SMIME_BUG_WARNING
	if ! set | grep '^ENABLE_SMIME_BUG_WARNING=' >/dev/null; then
		debug "warn" "(CFG): \$ENABLE_SMIME_BUG_WARNING variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(CFG): Setting \$ENABLE_SMIME_BUG_WARNING=1" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		ENABLE_SMIME_BUG_WARNING=1
	elif [ -z "${ENABLE_SMIME_BUG_WARNING}" ]; then
		debug "warn" "(CFG): \$ENABLE_SMIME_BUG_WARNING variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(CFG): Setting \$ENABLE_SMIME_BUG_WARNING=1" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		ENABLE_SMIME_BUG_WARNING=1
	elif [ "${ENABLE_SMIME_BUG_WARNING}" != "0" ] && [ "${ENABLE_SMIME_BUG_WARNING}" != "1" ]; then
		debug "warn" "(CFG): \$ENABLE_SMIME_BUG_WARNING variable has a wrong value: '${ENABLE_SMIME_BUG_WARNING}'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(CFG): Setting \$ENABLE_SMIME_BUG_WARNING=1" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		ENABLE_SMIME_BUG_WARNING=1
	fi

	debug "info"  "(OPT): Encryption enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "debug" "(OPT): Encryption algorithm: ${OPENSSL_ALGO_ARG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "debug" "(OPT): Encryption pub key: ${OPENSSL_PUBKEY_PEM}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

elif [ "${ENCRYPT}" = "0" ]; then
	debug "info" "(OPT): Encryption disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	ENCRYPT="0"
else
	debug "fatal" "(CFG): Invalid value for \$ENCRYPT: ${ENCRYPT}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): It is not clear whether or not you only allow encrypted dumps." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi



############################################################
# Deletion
############################################################

if ! set | grep '^DELETE=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$DELETE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DELETE=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${DELETE}" ]; then
	debug "warn" "(CFG): \$DELETE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DELETE=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${DELETE}" = "1"  ]; then

	# DELETE_METHOD
	if ! set | grep '^DELETE_METHOD=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$DELETE_METHOD variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		DELETE=0
		DELETE_METHOD=""
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${DELETE_METHOD}" ]; then
		debug "warn" "(CFG): \$DELETE_METHOD variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		DELETE=0
		DELETE_METHOD=""
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ "${DELETE_METHOD}" = "tmpwatch" ] && ! command -v tmpwatch > /dev/null 2>&1 ; then
		debug "err"  "(RUN): 'tmpwatch' not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "err"  "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		DELETE=0
		DELETE_METHOD=""
		MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
	elif [ "${DELETE_METHOD}" = "tmpreaper" ] && ! command -v tmpreaper > /dev/null 2>&1 ; then
		debug "err"  "(RUN): 'tmpreaper' not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "err"  "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		DELETE=0
		DELETE_METHOD=""
		MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
	elif [ "${DELETE_METHOD}" != "tmpwatch" ] && [ "${DELETE_METHOD}" != 'tmpreaper' ]; then
		debug "err"  "(CFG): \$DELETE_METHOD must be either 'tmpwatch' or 'tmpreaper' in ${CONFIG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "err"  "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		DELETE=0
		DELETE_METHOD=""
		MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
	else

		# DELETE_IF_OLDER
		if ! set | grep '^DELETE_IF_OLDER=' >/dev/null 2>&1; then
			debug "warn" "(CFG): \$DELETE_IF_OLDER variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			DELETE=0
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		elif [ -z "${DELETE_IF_OLDER}" ]; then
			debug "warn" "(CFG): \$DELETE_IF_OLDER variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			DELETE=0
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		elif ! valid_tmpwatch "${DELETE_IF_OLDER}" > /dev/null 2>&1 ; then
			debug "err"  "(CFG): \$DELETE_IF_OLDER does not have a valid ${DELETE_METHOD} value: ${DELETE_IF_OLDER}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "err"  "(HLP): See: man ${DELETE_METHOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "err"  "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			DELETE=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		fi
	fi

	# If deletion is still enabled
	if [ "${DELETE}" = "1"  ]; then

		debug "info"  "(OPT): Deletion enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "debug" "(OPT): Deleting files older than $(get_tmpwatch_value "${DELETE_IF_OLDER}") $(get_tmpwatch_unit_name "${DELETE_IF_OLDER}"). Using: ${DELETE_METHOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

		# DELETE_FORCE
		if ! set | grep '^DELETE_FORCE=' >/dev/null 2>&1; then
			debug "warn" "(CFG): \$DELETE_FORCE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(RUN): Setting \$DELETE_FORCE = 0" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			DELETE_FORCE=0
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		elif [ -z "${DELETE_FORCE}" ]; then
			debug "warn" "(CFG): \$DELETE_FORCE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(RUN): Setting \$DELETE_FORCE = 0" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			DELETE_FORCE=0
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		elif [ "${DELETE_FORCE}" = "1"  ]; then
			debug "trace" "(CFG): ${DELETE_METHOD}: Using '--force' option (deleting read-only files)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		elif [ "${DELETE_FORCE}" = "0"  ]; then
			debug "trace" "(CFG): ${DELETE_METHOD}: Not forcing (--force) deletion for read-only files" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		else
			debug "err" "(CFG): Invalid value for \$DELETE_FORCE: ${DELETE_FORCE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "err" "(RUN): Setting \$DELETE_FORCE = 0" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			DELETE_FORCE=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		fi
	fi

elif [ "${DELETE}" = "0"  ]; then
	debug "info" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "err" "(CFG): Invalid value for \$DELETE: ${DELETE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "err" "(OPT): Deletion disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DELETE=0
	MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
fi





############################################################
# Nagios
############################################################
# NAGIOS_LOG
if ! set | grep '^NAGIOS_LOG=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$NAGIOS_LOG variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	NAGIOS_LOG=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${NAGIOS_LOG}" ]; then
	debug "warn" "(CFG): \$NAGIOS_LOG variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	NAGIOS_LOG=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${NAGIOS_LOG}" = "1"  ]; then

	# NAGIOS_LOG_CHMOD
	if ! set | grep '^NAGIOS_LOG_CHMOD=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$NAGIOS_LOG_CHMOD variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(RUN): Setting default to ${_DEFAULT_NAGIOS_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		NAGIOS_LOG_CHMOD="${_DEFAULT_NAGIOS_LOG_CHMOD}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${NAGIOS_LOG_CHMOD}" ]; then
		debug "warn" "(CFG): \$NAGIOS_LOG_CHMOD variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(RUN): Setting default to ${_DEFAULT_NAGIOS_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		NAGIOS_LOG_CHMOD="${_DEFAULT_NAGIOS_LOG_CHMOD}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif ! valid_chmod "${NAGIOS_LOG_CHMOD}" > /dev/null 2>&1; then
		debug "err"  "(CFG): Invalid chmod value for \$NAGIOS_LOG_CHMOD: ${NAGIOS_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "err"  "(RUN): Setting default to ${_DEFAULT_NAGIOS_LOG_CHMOD}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		NAGIOS_LOG_CHMOD="${_DEFAULT_NAGIOS_LOG_CHMOD}"
		MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
	fi

	# NAGIOS_LOG_FILE
	if ! set | grep '^NAGIOS_LOG_FILE=' >/dev/null 2>&1; then
		debug "warn" "(CFG): \$NAGIOS_LOG_FILE variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		NAGIOS_LOG=0
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ -z "${NAGIOS_LOG_FILE}" ]; then
		debug "warn" "(CFG): \$NAGIOS_LOG_FILE variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		NAGIOS_LOG=0
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	elif [ ! -f "${NAGIOS_LOG_FILE}" ]; then
		debug    "warn" "(RUN): ${NAGIOS_LOG_FILE} not found" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug_pi "warn" "(RUN): Trying to create..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

		if ! touch "${NAGIOS_LOG_FILE}" > /dev/null 2>&1 ; then
			debug_i "err" "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "err" "(RUN): Failed to create file ${NAGIOS_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "err" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			NAGIOS_LOG=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		else
			debug_i  "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug    "warn" "(RUN): Created file ${NAGIOS_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug_pi "warn" "(RUN): Trying to chmod to ${NAGIOS_LOG_CHMOD}..." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

			if ! chmod ${NAGIOS_LOG_CHMOD} "${NAGIOS_LOG_FILE}" > /dev/null 2>&1 ; then
				debug_i "err"  "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				debug   "err"  "(RUN): Failed to chmod ${NAGIOS_LOG_CHMOD} ${NAGIOS_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				debug   "err"  "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				NAGIOS_LOG=0
				MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
			else
				debug_i "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			fi
		fi
	# Check if it has wrong permissions
	elif ! compare_chmod "$(get_file_chmod "${NAGIOS_LOG_FILE}")" "${NAGIOS_LOG_CHMOD}" > /dev/null 2>&1 ; then
		debug    "warn" "(RUN): Nagios log file has wrong permissions: $(get_file_chmod "${NAGIOS_LOG_FILE}"), but should be: ${NAGIOS_LOG_CHMOD} " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug_pi "warn" "(RUN): Trying to chmod to ${NAGIOS_LOG_CHMOD}... " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))

		if ! chmod "${NAGIOS_LOG_CHMOD}" "${NAGIOS_LOG_FILE}" > /dev/null 2>&1 ; then
			debug_i "err"  "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "err"  "(RUN): Failed to chmod ${NAGIOS_LOG_CHMOD} ${NAGIOS_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "err"  "(OPT): Nagios plugin log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			NAGIOS_LOG=0
			MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		else
			debug_i "warn" "OK\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi
	fi

	# Still enabled?
	if [ "${NAGIOS_LOG}" = "1"  ]; then
		if [ ! -r "${NAGIOS_LOG_FILE}" ]; then
			debug "warn" "(RUN): ${NAGIOS_LOG_FILE} not readable" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(HLP): FIX \NAGIOS_LOG_CHMOD value in config" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			NAGIOS_LOG=0
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		elif [ ! -w "${NAGIOS_LOG_FILE}" ]; then
			debug "warn" "(RUN): ${NAGIOS_LOG_FILE} not writeable" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(HLP): FIX \NAGIOS_LOG_CHMOD value in config" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			NAGIOS_LOG=0
			MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		else
			debug "info"  "(OPT): Nagios log enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "debug" "(OPT): Nagios log file: ${NAGIOS_LOG_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi
	fi

elif [ "${NAGIOS_LOG}" = "0"  ]; then
	debug "info" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "err" "(CFG): Invalid value for \$NAGIOS_LOG: ${NAGIOS_LOG}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "err" "(OPT): Nagios log disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DELETE=0
	MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
fi




############################################################
# Info file
############################################################

if ! set | grep '^DUMP_FILE_INFO=' >/dev/null 2>&1; then
	debug "warn" "(CFG): \$DUMP_FILE_INFO variable is not defined" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Info files disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_INFO=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ -z "${DUMP_FILE_INFO}" ]; then
	debug "warn" "(CFG): \$DUMP_FILE_INFO variable should not be empty" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Info files disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_INFO=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
elif [ "${DUMP_FILE_INFO}" = "1"  ]; then
	debug "info" "(OPT): Info files enabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
elif [ "${DUMP_FILE_INFO}" = "0"  ]; then
	debug "info" "(OPT): Info files disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "warn" "(CFG): Invalid value for \$DUMP_FILE_INFO: ${DUMP_FILE_INFO}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "warn" "(OPT): Info files disabled" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	DUMP_FILE_INFO=0
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi






############################################################
# Connection
############################################################


# Testing MySQL Connection
if ! _CONN_ERR="$( mysql_test_connection "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" )"; then
	debug "fatal" "(RUN): Cannot connect to mysql database." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(RUN): Via: $(which mysql) --defaults-file=${MYSQL_CNF_FILE} ${MYSQL_SSL_ARGS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(RUN): SQL: ${_CONN_ERR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "(HLP): Fix credentials in: ${MYSQL_CNF_FILE}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
else
	debug "trace" "(RUN): MySQL connection test successful." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi


# Getting MySQL Server Status
if ! MYSQL_SERVER_STATUS="$( get_mysql_server_status "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" )"; then
	debug "fatal" "(SRV): Cannot retrieve MySQL server status" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi


# MySQL TCP/IP or Socket connection
debug "debug"  "(SRV): MySQL server connection: $( get_mysql_connection_type_info "${MYSQL_SERVER_STATUS}" )" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


# Testing MySQL for SSL connection
MYSQL_SSL_STATUS="$( get_mysql_connection_ssl_info "${MYSQL_SERVER_STATUS}" )"
if [ "${MYSQL_SSL_STATUS}" = "Not in use" ]; then
	if [ "${MYSQL_SSL_ENABLE}" = "1" ]; then
		debug "fatal" "(SRV): MySQL server connection: Not using SSL, but demanded." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "(HLP): We have already made some plain-text connections" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "(HLP): It is advised to change your password immediately" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	else
		debug "debug"  "(SRV): MySQL server connection: Not using SSL (plain)." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	fi
else
	debug "debug" "(SRV): MySQL server connection: Using SSL (${MYSQL_SSL_STATUS})" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi

# Testing MySQL for Master or Slave
if ! MYSQL_SERVER_TYPE="$(get_mysql_server_replication_type "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}")"; then
	debug "fatal" "(RUN): Connection error" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
else
	debug "debug" "(SRV): MySQL server rep type:   ${MYSQL_SERVER_TYPE}"  "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi


# Get MySQL Name and Version
MYSQL_SERVER="$( get_mysql_server_name "${MYSQL_SERVER_STATUS}" )"
MYSQL_VERSION="$( get_mysql_server_version "${MYSQL_SERVER_STATUS}" )"

# Get MySQL Server Hostname and Port
MYSQL_HOST="$( get_mysql_server_hostname "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" )"
MYSQL_PORT="$( get_mysql_server_port "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" )"

debug "debug" "(SRV): MySQL server version:    $( [ "${MYSQL_SERVER}" != "" ] && echo "${MYSQL_SERVER} " )${MYSQL_VERSION}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
debug "debug" "(SRV): MySQL server hostname:   ${MYSQL_HOST}:${MYSQL_PORT}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"











############################################################
# Test mode
############################################################
if [ "${_ARG_TEST}" = "1" ]; then
	debug "info" "(ARG): Exiting program from test mode (--test)." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_OK
fi















####################################################################################################
####################################################################################################
##
##  M A I N   E N T R Y   P O I N T   ( R U N )
##
####################################################################################################
####################################################################################################


################################################################################
#
# VARIABLES
#
################################################################################

#
# Binaries
#
BIN_TMPWATCH="$(which "${DELETE_METHOD}" 2>/dev/null)"	# This can be tmpwatch or tmpreaper

#
# Database counters
#
DB_CNT_TOTAL=0			# Total numbers of databases
DB_CNT_IGNORED=0		# Number of ignored databases (empty or via ignore list)
DB_CNT_DUMPED=0			# Number of successfully dumped databases
DB_CNT_FAILED=0			# Number of unseccessfull dumped fatabases (failed)
DB_CNT_REQ_FAILED=0		# Number of required databases not dumped

#
# Database lists
#
DB_LIST_ALL=""			# Newline separated list of all databases
DB_LIST_DUMPED=""		# Comma separated list of all dumped databases
DB_LIST_IGNORED=""		# Comma separated list of all ignored databases
DB_LIST_FAILED=""		# Comma separated list of all failed databases
DB_LIST_REQ_FAILED=""	# Comma separated list of required databases not dumped

#
# Total size of all dumped databases
#
TOTAL_SIZE_B=0
TOTAL_SIZE_MB=0

#
# Overall Timing
#
TIME_TOTAL_START=0
TIME_TOTAL_DURATION=0



#
# Nagios Log: Aggregated exit code (0:OK, 1:Warn, 2:ERR, 3:UNKNOWN)
#
NAGIOS_EXIT_CODE=0



#
# TODO: Make all vars within 'if' local and small _tmp_bla
#
#


################################################################################
#
# PRE-PROCESSING
#
################################################################################



############################################################
# Create tmp files and folders
############################################################

# New temporary directory
if MY_TMP_DIR="$(create_tmp_dir "${TMP_DIR}/${INFO_NAME}.XXXXXXXXXX" "0700")"; then
	debug "trace"  "(RUN): Creating tmp dir:  ${MY_TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "fatal" "(RUN): Cannot create tmp dir:  ${MY_TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_ABORT
fi

# Temp file 'tmpwatch/tmpreaper'
if [ "${DELETE}" = "1" ]; then
	if MY_TMP_FILE_DEL="$(create_tmp_file "${MY_TMP_DIR}/.XXXXXXXXXXXXXXX" "0600")"; then
		debug "trace"  "(RUN): Creating tmp file: ${MY_TMP_FILE_DEL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	else
		debug "fatal" "(RUN): Cannot create tmp file: ${MY_TMP_FILE_DEL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "fatal" "Aborting" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		exit $EXIT_ABORT
	fi
fi


############################################################
# Get all databases
############################################################

debug_pi "debug" "(SQL): Retrieving list of databases... " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

# Get all databases
DB_LIST_ALL="$( get_mysql_databases "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" )"
DB_CNT_TOTAL="$( str_count_lines "${DB_LIST_ALL}" )"

debug_i "debug" "${DB_CNT_TOTAL}\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"





################################################################################
#
# DUMP DATABASES
#
################################################################################


i=0 # Lopp counter
TIME_TOTAL_START="$( get_time )"
for db in ${DB_LIST_ALL}; do


	##########################################################
	# VARIABLES

	# Increment Loop counter
	i=$((i + 1))

	# Show progress
	# At which database are we right now?
	# Left-space-padded " 1/10" counter
	_cnt="$(printf "%$(str_len "${DB_CNT_TOTAL}")d/%d" "${i}" "${DB_CNT_TOTAL}")"

	# File extension for dump file (.sql, .sql.gz, .sql.gz.enc or .sql.enc)
	ext="$( build_file_extension "${ENCRYPT}" "${COMPRESS}" "${COMPRESS_EXT}" )"

	# Skip database vars
	skip_empty=0
	skip_ignored=0





	##########################################################
	# IGNORE CHECKS


	# Empty databases
	if mysql_database_is_empty "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" "${db}"; then
		skip_empty=1
	fi

	# Ignored database
	if _ignore_pattern="$( database_is_ignored "${IGNORE}" "${db}" )"; then
		skip_ignored=1
		debug "trace" "(CFG): ${_cnt} Ignoring DB: \"${db}\" by \$IGNORE pattern: \"${_ignore_pattern}\""  "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

		# Only if ignored database is not required, we can ignore it.
		if database_is_required "${REQUIRE}" "${db}"; then
			skip_ignored=0
			debug "trace" "(CFG): ${_cnt} Required DB: \"${db}\" un-ignored by \$REQUIRE settings"  "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi
	fi


	#
	# Main case 1 (skipping)
	#
	if [ "${skip_empty}" = "1" ] || [ "${skip_ignored}" = "1" ]; then

		# Case 1/3: Empty AND Ignored
		if [ "${skip_empty}" = "1" ] && [ "${skip_ignored}" = "1" ]; then
			debug "info" "(SQL): ${_cnt} Skipping: ${db} (DB is empty and ignored)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		# Case 2/3: Empty
		elif [ "${skip_empty}" = "1" ]; then
			debug "info" "(SQL): ${_cnt} Skipping: ${db} (DB is empty)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		# Case 3/3:Ignored
		else
			debug "info" "(SQL): ${_cnt} Skipping: ${db} (DB is ignored)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi

		DB_CNT_IGNORED=$((DB_CNT_IGNORED + 1))
		DB_LIST_IGNORED="$( str_join "${DB_LIST_IGNORED}" "," "${db}" )"

	#
	# Main case 2 (file exists on disk)
	#
	elif [ -f "${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}" ]; then

		# Increment counters
		DB_CNT_FAILED=$((DB_CNT_FAILED + 1))
		MDS_FAIL_COUNT=$((MDS_FAIL_COUNT + 1))

		# Add failed db to nagios log
		DB_LIST_FAILED="$( str_join "${DB_LIST_FAILED}" "," "${db}" )"

		debug "fatal" "(RUN): ${_cnt} Failed:   ${db} cannot be written to disk (file exists: ${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext})" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		NAGIOS_EXIT_CODE="$(merge_exit_codes "${NAGIOS_EXIT_CODE}" 2)"

	#
	# Main case 3 (dumping)
	#
	else

		##########################################################
		# VARIABLES

		# Database size
		DB_SIZE_B="$( get_mysql_database_size "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" "${db}" )"
		DB_SIZE_MB="$( div "${DB_SIZE_B}" "1048576" )"
		DB_SIZE_MB="$( round "${DB_SIZE_MB}" "2" )"

		# Total Database size (aggregated db size)
		TOTAL_SIZE_B=$( sum "${TOTAL_SIZE_B}" "${DB_SIZE_B}" )

		# Number of tables
		TBL_CNT_TOTAL="$(  count_total_tables      "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" "${db}" )"
		TBL_CNT_INNODB="$( count_innodb_tables     "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" "${db}" )"
		TBL_CNT_OTHERS="$( count_non_innodb_tables "${MYSQL_CNF_FILE}" "${MYSQL_SSL_ARGS}" "${db}" )"

		debug "trace" "(RUN): ${_cnt} Number of total tables:       ${TBL_CNT_TOTAL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "trace" "(RUN): ${_cnt} Number of InnoDB tables:      ${TBL_CNT_INNODB}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "trace" "(RUN): ${_cnt} Number of non-InnoDB tables:  ${TBL_CNT_OTHERS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


		##########################################################
		# MYSQLDUMP OPTIONS

		#
		# CONSISTENCY OPTIONS (By Table Engines)
		#
		# information_schema and performance schema are special tables, which do not allow locking
		if [ "${db}" != "information_schema" ] && [ "${db}" != "performance_schema" ]; then
			MYSQL_LOCK_OPTS="$( get_consistency_opts "${CONSISTENT_DUMP_ONLY_INNODB}" "${CONSISTENT_DUMP_MIXED_INNODB}" "${CONSISTENT_DUMP_NO_INNODB}" "${TBL_CNT_INNODB}" "${TBL_CNT_OTHERS}" )"
			debug "trace" "(RUN): ${_cnt} Applying consistency setting: ${MYSQL_LOCK_OPTS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		else
			MYSQL_LOCK_OPTS="--skip-lock-tables"
			debug "trace" "(RUN): ${_cnt} \"${db}\" is special and needs '--skip-lock-tables'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi

		#
		# SPECIAL OPTIONS (By Virtual Databases)
		#
		MYSQL_SPECIAL_OPTS=""

		# Performance schema does not allow to dump events
		# mysqldump: mysqldump  Couldn't execute 'show events'  Access denied for user 'root'@'localhost' to database 'performance_schema' (1044)
		if [ "${db}" = "performance_schema" ]; then
			MYSQL_SPECIAL_OPTS="--skip-events"
			debug "trace" "(RUN): ${_cnt} \"${db}\" cannot be dumped with --events. Adding --skip-events" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi

		#
		# QUICK OPTION (--quick/--skip-quick dependent on size)
		#
		MYSQL_QUICK_OPT="$(echo "${DB_SIZE_MB} ${MYSQL_OPTS_QUICK_MIN_SIZE}" | awk '{print ($1 >= $2) ? "--quick" : "--skip-quick" }')"

		#
		# FINAL OPTIONS (Build final MySQLDump arguments)
		#
		MYSQL_ARGUMENTS="--defaults-file=${MYSQL_CNF_FILE} ${MYSQL_SSL_ARGS}"
		MYSQLDUMP_ARGUMENTS="${MYSQL_OPTS} ${MYSQL_LOCK_OPTS} ${MYSQL_SPECIAL_OPTS} ${MYSQL_QUICK_OPT}"



		##########################################################
		# SMIME WARNING
		# https://github.com/cytopia/mysqldump-secure/issues/21

		# Warn about SMIME bug for files > 1200MB
		SMIME_CRITICAL_SIZE_MB="1200"
		if [ "${ENCRYPT}" = "1" ] && [ "${ENABLE_SMIME_BUG_WARNING}" = "1" ] && [ "$( printf "%.0f\n" "${DB_SIZE_MB}" )" -gt "${SMIME_CRITICAL_SIZE_MB}" ]; then
			debug "warn" "(SQL): ${_cnt} Warning:  Encryption is enabled and database size is > ${SMIME_CRITICAL_SIZE_MB} MB" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(SQL): ${_cnt} Warning:  Verify that your backup can be decrypted." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(SQL): ${_cnt} Warning:  This warning can be disabled via 'ENABLE_SMIME_BUG_WARNING=0'" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug "warn" "(SQL): ${_cnt} Warning:  Read here: https://github.com/cytopia/mysqldump-secure/issues/21" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		fi


		##########################################################
		# MAKE THE BACKUPS

		debug_pi "info" "(SQL): ${_cnt} Dumping:  ${db} (${DB_SIZE_MB} MB) " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


		#
		# Stop time (start)
		#
		starttime="$( get_time )"

		#
		# Case 1/4 Plain
		#
		if [ "${COMPRESS}" = "0" ] && [ "${ENCRYPT}" = "0" ];  then

			debug_i "debug"  "$( [ "${MYSQL_LOCK_OPTS}" != "" ] && echo "(${MYSQL_LOCK_OPTS}) ")$( [ "${MYSQL_SPECIAL_OPTS}" != "" ] && echo "(${MYSQL_SPECIAL_OPTS}) ")(${MYSQL_QUICK_OPT}) " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			# Run Compressed & Encrypted
			error_statuses="$(run_mysqldump "0" "${MYSQL_ARGUMENTS} ${MYSQLDUMP_ARGUMENTS}" "${db}" \
				"${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}" "${DUMP_FILE_CHMOD}" \
				"${COMPRESS_BIN}" "${COMPRESS_ARG}" \
				"${OPENSSL_ALGO_ARG}" "${OPENSSL_PUBKEY_PEM}" \
				"${MY_TMP_DIR}"
			)"
		#
		# Case 2/4 Compressed
		#
		elif [ "${COMPRESS}" = "1" ] && [ "${ENCRYPT}" = "0" ];  then

			debug_i "debug"  "(compressed) $( [ "${MYSQL_LOCK_OPTS}" != "" ] && echo "(${MYSQL_LOCK_OPTS}) ")$( [ "${MYSQL_SPECIAL_OPTS}" != "" ] && echo "(${MYSQL_SPECIAL_OPTS}) ")(${MYSQL_QUICK_OPT}) " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			# Run Compressed & Encrypted
			error_statuses="$(run_mysqldump "1" "${MYSQL_ARGUMENTS} ${MYSQLDUMP_ARGUMENTS}" "${db}" \
				"${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}" "${DUMP_FILE_CHMOD}" \
				"${COMPRESS_BIN}" "${COMPRESS_ARG}" \
				"${OPENSSL_ALGO_ARG}" "${OPENSSL_PUBKEY_PEM}" \
				"${MY_TMP_DIR}"
			)"
		#
		# Case 3/4 Encrypted
		#
		elif [ "${COMPRESS}" = "0" ] && [ "${ENCRYPT}" = "1" ];  then

			debug_i "debug"  "(encrypted) $( [ "${MYSQL_LOCK_OPTS}" != "" ] && echo "(${MYSQL_LOCK_OPTS}) ")$( [ "${MYSQL_SPECIAL_OPTS}" != "" ] && echo "(${MYSQL_SPECIAL_OPTS}) ")(${MYSQL_QUICK_OPT}) " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			# Run Compressed & Encrypted
			error_statuses="$(run_mysqldump "2" "${MYSQL_ARGUMENTS} ${MYSQLDUMP_ARGUMENTS}" "${db}" \
				"${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}" "${DUMP_FILE_CHMOD}" \
				"${COMPRESS_BIN}" "${COMPRESS_ARG}" \
				"${OPENSSL_ALGO_ARG}" "${OPENSSL_PUBKEY_PEM}" \
				"${MY_TMP_DIR}"
			)"
		#
		# Case 4/4 Compressed && Encrypted
		#
		elif [ "${COMPRESS}" = "1" ] && [ "${ENCRYPT}" = "1" ];  then

			debug_i "debug"  "(compressed) (encrypted) $( [ "${MYSQL_LOCK_OPTS}" != "" ] && echo "(${MYSQL_LOCK_OPTS}) " )$( [ "${MYSQL_SPECIAL_OPTS}" != "" ] && echo "(${MYSQL_SPECIAL_OPTS}}) ")(${MYSQL_QUICK_OPT}) " "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			# Run Compressed & Encrypted
			error_statuses="$(run_mysqldump "3" "${MYSQL_ARGUMENTS} ${MYSQLDUMP_ARGUMENTS}" "${db}" \
				"${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}" "${DUMP_FILE_CHMOD}" \
				"${COMPRESS_BIN}" "${COMPRESS_ARG}" \
				"${OPENSSL_ALGO_ARG}" "${OPENSSL_PUBKEY_PEM}" \
				"${MY_TMP_DIR}"
			)"
		#
		# Case 5/4: WRONG OPTIONS
		#
		else
			debug "fatal" "(???) Internal Error. Wrong mysqldump choice." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			_abort_on_internal_error
		fi

		#
		# Stop time (get duration)
		#
		duration="$( get_duration "${starttime}" )"



		#
		# Pipefail Errors
		#
		_dump_failed=0
		if ! MY_ERRORS="$(mysqldump_pipefail_errors "${error_statuses}")"; then
			_dump_failed=1

			debug_i "fatal" "Failed\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "fatal" "(RUN): ${_cnt} Error dumping ${db}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			if ! MY_WARNINGS="$(mysqldump_pipefail_warnings "${error_statuses}")"; then
				debug "warn" "(RUN): ${_cnt} Warning dumping ${db}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				debug "warn" "${MY_WARNINGS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
			fi
			debug  "fatal"  "${MY_ERRORS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			DB_CNT_FAILED=$((DB_CNT_FAILED + 1))
			MDS_FAIL_COUNT=$((MDS_FAIL_COUNT + 1))
			NAGIOS_EXIT_CODE="$(merge_exit_codes "${NAGIOS_EXIT_CODE}" 2)"
			DB_LIST_FAILED="$( str_join "${DB_LIST_FAILED}" "," "${db}" )"
		#
		# Pipefail OK
		#
		else
			_dump_failed=0

			debug_i "ok"    "${duration} sec ($(get_file_size "${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}" "m") MB)\n" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
			debug   "trace" "(RUN): ${_cnt} Dump File: ${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}"  "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

			if ! MY_WARNINGS="$(mysqldump_pipefail_warnings "${error_statuses}")"; then
				debug "warn" "(RUN): ${_cnt} Warning dumping ${db}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				debug "warn" "${MY_WARNINGS}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
			fi

			DB_CNT_DUMPED=$((DB_CNT_DUMPED + 1))
			DB_LIST_DUMPED="$( str_join "${DB_LIST_DUMPED}" "," "${db}" )"
		fi



		#
		# Success Handler
		#
		if [ "${_dump_failed}" = "0" ]; then

			#
			# If all was good, remove required db from list
			#
			REQUIRE="$( str_remove_word "${REQUIRE}" "${db}" )"


			#
			# Write file info?
			#
			if [ "${DUMP_FILE_INFO}" = "1" ]; then

				write_info_file "${DUMP_DIR}" "${DUMP_FILE_PRE}${db}${ext}" "${DUMP_FILE_CHMOD}" \
					"${db}" "${DB_SIZE_B}" "${TBL_CNT_TOTAL}" "${MYSQLDUMP_ARGUMENTS}" "${duration}" \
					"${COMPRESS}" "${COMPRESS_BIN}" "${COMPRESS_ARG}" \
					"${ENCRYPT}" "${OPENSSL_ALGO_ARG}" "${OPENSSL_PUBKEY_PEM}" \
					"${MYSQL_ARGUMENTS}" "${MYSQL_SSL_ENABLE}" "${MYSQL_SSL_STATUS}" \
					"${MYSQL_SERVER_STATUS}" "${MYSQL_HOST}" "${MYSQL_PORT}" "${MYSQL_SERVER_TYPE}" "${MYSQL_SERVER}" "${MYSQL_VERSION}"

				if [ "$?" != "0" ]; then
					debug "err"   "(RUN): ${_cnt} Could not write info File: ${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}.info" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
					MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
				else
					debug "trace" "(RUN): ${_cnt} Info File: ${DUMP_DIR}/${DUMP_FILE_PRE}${db}${ext}.info" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
				fi

			fi
		fi
	fi
done
TIME_TOTAL_DURATION="$( get_duration "${TIME_TOTAL_START}" )"




################################################################################
#
# POST PROCESSING
#
################################################################################


############################################################
# (CFG) Check required databases
############################################################

for req in ${REQUIRE}; do
	debug "err" "(RUN): Required database: \"${req}\" has not been dumped." "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MDS_FAIL_COUNT=$((MDS_FAIL_COUNT + 1))
	DB_CNT_REQ_FAILED=$((DB_CNT_REQ_FAILED + 1))
	DB_LIST_REQ_FAILED="$( str_join "${DB_LIST_REQ_FAILED}" "," "${req}" )"
	NAGIOS_EXIT_CODE="$(merge_exit_codes "${NAGIOS_EXIT_CODE}" 2)"
done




############################################################
# (OPT) TMPWATCH (Delete old files)
############################################################
TMPWATCH_NUM_DEL="0"
TMPWATCH_NUM_IGN="0"

if [ "${DELETE}" = "1" ]; then

	# Force deletion?
	if [ "${DELETE_FORCE}" = "1" ]; then FORCE_ARG="--force"; else FORCE_ARG=""; fi

	# Verbosity
	if [ "${DELETE_METHOD}" = "tmpwatch" ]; then TMPWATCH_OPTS="-vv"; else TMPWATCH_OPTS="--verbose=3 --showdeleted"; fi

	TMPWATCH_RUN="$(${BIN_TMPWATCH} ${FORCE_ARG} -m "${DELETE_IF_OLDER}" "${TMPWATCH_OPTS}" "${DUMP_DIR}/" 2> "${MY_TMP_FILE_DEL}")"
	TMPWATCH_ERRNO="$?"
	TMPWATCH_ERROR="$( cat "${MY_TMP_FILE_DEL}" )"

	if [ "${TMPWATCH_ERRNO}" != "0" ]; then
		debug "err"  "(RUN): ${DELETE_METHOD} exit code: ${TMPWATCH_ERRNO}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "err"  "(RUN): ${DELETE_METHOD} error: ${TMPWATCH_ERROR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MDS_ERROR_COUNT=$((MDS_ERROR_COUNT + 1))
		NAGIOS_EXIT_CODE="$(merge_exit_codes "${NAGIOS_EXIT_CODE}" 1)"
	elif [ "${TMPWATCH_ERROR}" != "" ]; then
		debug "warn" "(RUN): ${DELETE_METHOD} exit code: ${TMPWATCH_ERRNO}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "(RUN): ${DELETE_METHOD} error: ${TMPWATCH_ERROR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		NAGIOS_EXIT_CODE="$(merge_exit_codes "${NAGIOS_EXIT_CODE}" 1)"
	fi

	# Number removed/skipped files
	TMPWATCH_NUM_DEL="$( echo "${TMPWATCH_RUN}" | grep -ci 'Removing file' )"
	TMPWATCH_NUM_IGN="$( echo "${TMPWATCH_RUN}" | grep -ci 'skipped:' )"

	# Output of removed/skipped files
	TMPWATCH_TXT_DEL="$( echo "${TMPWATCH_RUN}" | grep -i 'Removing file' )"
	TMPWATCH_TXT_IGN="$( echo "${TMPWATCH_RUN}" | grep -i 'skipped:' )"

	# Number of deleted files and the command itself
	debug  "info"  "(RUN): Deleting files older than $(get_tmpwatch_value "${DELETE_IF_OLDER}") $(get_tmpwatch_unit_name "${DELETE_IF_OLDER}") ... ${TMPWATCH_NUM_DEL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	debug  "trace" "(CFG): ${BIN_TMPWATCH} ${FORCE_ARG} -m ${DELETE_IF_OLDER} ${TMPWATCH_OPTS} ${DUMP_DIR}/" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"


	# Some files have been skipped!
	if [ "${TMPWATCH_NUM_IGN}" != "0" ] || [ "${TMPWATCH_TXT_IGN}" != "" ]; then
		debug "warn" "(RUN): ${DELETE_METHOD}: ${TMPWATCH_NUM_IGN} files could not be deleted:" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		debug "warn" "${TMPWATCH_TXT_IGN}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
		NAGIOS_EXIT_CODE="$(merge_exit_codes "${NAGIOS_EXIT_CODE}" 1)"
	fi

	# Show deleted files
	debug "info|(RUN): ${DELETE_METHOD}: " "${TMPWATCH_TXT_DEL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

	# Show full trace output
	debug "trace|(RUN): ${DELETE_METHOD}: " "${TMPWATCH_RUN}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
fi



############################################################
# (OPT) Nagios Plugin Log
############################################################

if [ "${NAGIOS_LOG}" = "1" ]; then

	debug "debug" "(RUN): Writing nagios log file" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"

	{
		echo "[state]";
		echo "success = ${NAGIOS_EXIT_CODE}";
		echo "lastbak = ${TIME_TOTAL_START}";
		echo "message = ";
		echo "missing = ${DB_LIST_REQ_FAILED}";
		echo;
		echo "[options]";
		echo "opt_log = ${LOG}";
		echo "opt_com = ${COMPRESS}";
		echo "opt_enc = ${ENCRYPT}";
		echo "opt_del = ${DELETE}";
		echo;
		echo "[messages]";
		echo "msg_dbs = ${DB_CNT_DUMPED}";
		echo "msg_ign = ${DB_CNT_IGNORED}";
		echo "msg_err = ${DB_CNT_FAILED}";
		echo "msg_meg = ${TOTAL_SIZE_MB}";
		echo "msg_sec = ${TIME_TOTAL_DURATION}";
		echo "msg_del = ${TMPWATCH_NUM_DEL}";
		echo;
		echo "[tmpwatch]";
	} > "${NAGIOS_LOG_FILE}"

	if [ "${DELETE}" = "1" ]; then
		{
			echo "del_time = ${DELETE_IF_OLDER}";
			echo "del_del  = ${TMPWATCH_NUM_DEL}";	# how many files deleted
			echo "del_skp  = ${TMPWATCH_NUM_IGN}";	# how many files skipped (due to unable to delete)
		} >> "${NAGIOS_LOG_FILE}"
	else
		{
			echo "del_time = 0";
			echo "del_del  = 0";	# how many files deleted
			echo "del_skp  = 0";	# how many files skipped (due to unable to delete)
		} >> "${NAGIOS_LOG_FILE}"
	fi

	{
		echo;
		echo "[stats]";
		echo "db_dumped  = ${DB_LIST_DUMPED}";
		echo "db_error   = ${DB_LIST_FAILED}";
		echo "db_ignored = ${DB_LIST_IGNORED}";
		echo;
		echo "[misc]";
		echo "version = ${INFO_VERSION}";	# mysqldump-secure version (for checking against latest)
	} >> "${NAGIOS_LOG_FILE}"
fi



############################################################
# Delete temporary files
############################################################

#
# Delete tmp files/folders in reverse order
#
if [ "${DELETE}" = "1" ]; then
	if rm "${MY_TMP_FILE_DEL}" 2>/dev/null; then
		debug "trace" "(RUN): Deleting tmp file: ${MY_TMP_FILE_DEL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	else
		debug "warn"  "(RUN): Cannot delete tmp file: ${MY_TMP_FILE_DEL}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
		MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
	fi
fi

if rm -rf "${MY_TMP_DIR}" 2>/dev/null; then
	debug "trace" "(RUN): Deleting tmp dir:  ${MY_TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
else
	debug "warn"  "(RUN): Cannot delete tmp dir:  ${MY_TMP_DIR}" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	MDS_WARNING_COUNT=$((MDS_WARNING_COUNT + 1))
fi



############################################################
# Exit
############################################################


# Human readable output of TOTAL_SIZE (MegaBytes)
TOTAL_SIZE_MB="$( div "${TOTAL_SIZE_B}" "1048576" )"
TOTAL_SIZE_MB="$( round "${TOTAL_SIZE_MB}" "2" )"

debug "debug" "(RUN): Dumping finished (OK: ${DB_CNT_DUMPED} dbs, IGN: ${DB_CNT_IGNORED} dbs, ERR: ${DB_CNT_FAILED}, TOTAL: ${DB_CNT_TOTAL})" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
debug "debug" "(RUN): Took ${TIME_TOTAL_DURATION} seconds" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
debug "debug" "(RUN): Total size dumped: ${TOTAL_SIZE_MB} MB" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"



if [ "${MDS_FAIL_COUNT}" != "0" ]; then
	if [ "${MDS_FAIL_COUNT}" = "1" ]; then
		debug "fatal" "Finished with ${MDS_FAIL_COUNT} Failures. ${DB_CNT_FAILED} failed backups (${DB_CNT_REQ_FAILED} required dbs missing)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	else
		debug "fatal" "Finished with ${MDS_FAIL_COUNT} Failures. ${DB_CNT_FAILED} failed backups (${DB_CNT_REQ_FAILED} required dbs missing)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	fi
	exit $EXIT_FAIL
elif [ "${MDS_ERROR_COUNT}" != "0" ]; then
	if [ "${MDS_ERROR_COUNT}" = "1" ]; then
		debug "err"  "Finished with ${MDS_ERROR_COUNT} error. (Backups done successfully)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	else
		debug "err"  "Finished with ${MDS_ERROR_COUNT} errors. (Backups done successfully)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	fi
	exit $EXIT_ERROR
elif [ "${MDS_WARNING_COUNT}" != "0" ]; then
	if [ "${MDS_WARNING_COUNT}" = "1" ]; then
		debug "warn"  "Finished with ${MDS_WARNING_COUNT} warning. (Backups done successfully)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	else
		debug "warn"  "Finished with ${MDS_WARNING_COUNT} warnings. (Backups done successfully)" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	fi
	exit $EXIT_WARNING
else
	debug "ok" "Finished successfully" "${OUT_VERBOSITY}" "${LOG_VERBOSITY}" "${LOG_FILE}"
	exit $EXIT_OK
fi
