#!/bin/sh
# devel.a0fs.net: zfsbackup.lib.log - v0.1 by awgur
# ---
# Файл содержит процедуры и функции организации определения и исполнения
# заданий на резервное копирование наборов данных ZFS

# Определяем корень иерархии файлов относительно файла задания $dir_root/task/$task_name
# при условии что данная переменная не определена в вызывающем скрипте
dir_root="${dir_root:-$(dirname "$(dirname "$(readlink -f "$0")")")}"
dir_lib="${dir_lib:-${dir_root}/lib}"  # Директория с библиотеками

. "${dir_lib}/log.sh"

sys_propNamePrefix="net.a0fs.zfsbackup"  # префикс для свойств набора данных ZFS,
                                         # в которые будут сохранятся параметры

sys_backupType_d="daily"
sys_backupType_h="hourly"
sys_date_now="$(date '+%Y%m%d-%H%M%S')"
sys_date_today="$(date '+%Y%m%d')"
sys_date_thisHour="$(date '+%Y%m%d-%H')"

svc_isNum () {
	# Проверяет, является ли аргумент числом
	echo "$1" | grep -vE '^[[:space:]]*$' | sed 's/[[:space:]]//g' | tr -d '\n\r' | grep -E '^[0-9]+$' > /dev/null 2>&1
}

svc_makeName () {
	# Конструктор имени снимка. Для однотипного именования по всему скрипту
	# Аргументы:
	#   dataset_name : Имя набора данных, для которого получаем снимок
	#   backup_type  : Имя типа резервного копирования
	#   timeMark     : Метка времени
	if ! [ "$1" -a "$2" -a "$3" ] ; then
		log_err "svc_makeName(): Not enough arguments (dataset_name='$1', backup_type='$2', timeMark='$3')"
		return 1
	fi
	echo "$1@${sys_propNamePrefix}_${3}_${2}"
}

svc_date_getSnapTime () {
	# Получение временной метки из имени снимка
	local buf
	if ! [ "$1" ] ; then
		return 1
	fi

	echo "$1" | sed "s/.*@${sys_propNamePrefix}_//" | cut -f 1 -d '_'
}

svc_date_isToday () {
	# Проверка, сделан ли снимок набора данных в день проверки
	local buf
	if ! [ "$1" ] ; then
		return 1
	fi

	svc_date_getSnapTime "$1" | grep -E "^${sys_date_today}" >/dev/null 2>&1
}

svc_date_isThisHour () {
	# Проверка, сделан ли снимок набора данных в час проверки
	local buf
	if ! [ "$1" ] ; then
		return 1
	fi

	svc_date_getSnapTime "$1" | grep -E "^${sys_date_thisHour}" >/dev/null 2>&1
}

zfs_getSnaps () {
	# Получить список снимков для данного в аргументах набора.
	# ВНИМАНИЕ! Снимки сортируются в обратном порядке
	# Аргументы:
	#   dataset_name : Имя набора, для которго получаем снимки
	#   backup_type  : Имя типа резервного копирования
	if ! [ "$1" -a "$2" ] ; then
		log_err "zfs_getSnaps(): Dataset or backup type not set: dataset='$1' backup_type='$2'"
		return 1
	fi

	zfs list -Ho name -d 1 -r -t snapshot "$1" \
		| grep -E "@${sys_propNamePrefix}_[0-9]{8}-[0-9]{6}_${2}\$" \
		| sort -r
}

zfs_getLastSnap () {
	# Получить последний снимок созданный системой резервного копирования
	# Аргументы:
	#  dataset_name : Имя набора, для которого получаем снимок
	if ! [ "$1" ] ; then
		log_err "zfs_getLastSnap(): Dataset not set"
		return 1
	fi

	zfs list -Ho name -d 1 -r -t snapshot "$1" \
		| grep -E "@${sys_propNamePrefix}_[0-9]{8}-[0-9]{6}_" \
		| sort -r | head -n 1
}

zfs_getProp () {
	# Получить значение свойства из zfs, относящиеся к системе резервного копирования
	# Аргументы:
	#  dataset_name : Имя набора данных
	#  prop_name    : Имя свойства
	local res

	if ! [ "$1" -a "$2" ] ; then
		return 1
	fi
	res=$(zfs get -Hp -o value "${sys_propNamePrefix}:$2" "$1")

	if [ "${res}" = "-" ] ; then
		res=""
	fi
	echo "$res"
}

zfs_setProp () {
	# Установить значение свойства в zfs, относящиеся к системе резервного копирования
	# Аргументы:
	#  dataset_name : Имя набора данных
	#  prop_name    : Имя свойства
	#  prop_value   : Значение свойства
	if ! [ "$1" -a "$2" -a "$3" ] ; then
		return 1
	fi

	zfs set "${sys_propNamePrefix}:$2"="$3" "$1"
	log "zfs_setProp(): Property changed for '$1': user='$(whoami 2>/dev/null)' uid='$(id -u)' property='$2' value='$3'"
}

zfs_isBackup () {
	# Необходимо ли резервное копирование для данного набора данных
	# Аргументы:
	#  dataset_name : Имя исследуемого набора данных
	case "$(zfs_getProp "$1" "enable")" in
		yes|y )
			return 0
		;;
		* )
			return 1
		;;
	esac
}

zfs_clean () {
	# Удаление устаревших резервных копий
	# Аргументы:
	#  dataset_name : Набор данных для которого производится операция
	#  backup_type  : Тип резервной копии
	#  count        : Количество копий, которые необходимо сохранить
	local cnt

	if ! svc_isNum "$3" ; then
		log_err "zfs_clean(): Wrong backup count: %3 (dataset_name='$1', backup_type='$2')"
		return 1
	fi
	cnt="$(( $cnt + 1 ))"

	zfs_getSnaps "$1" "$2" \
		| tail -n "+${cnt}" | xargs -n 1 zfs destroy -v \
		| awk "{print \"$1: \" \$0 }"  | log
}
