#!/bin/sh
#
# This file is part of OpenMediaVault.
#
# @license   http://www.gnu.org/licenses/gpl.html GPL Version 3
# @author    Volker Theile <volker.theile@openmediavault.org>
# @copyright Copyright (c) 2009-2015 Volker Theile
#
# OpenMediaVault is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# OpenMediaVault is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with OpenMediaVault. If not, see <http://www.gnu.org/licenses/>.

# Documentation/Howto:
# http://linux.die.net/man/7/udev
# http://wiki.ubuntuusers.de/udev
# https://wiki.archlinux.org/index.php/Udev#Mount_under_.2Fmedia.3B_use_partition_label_if_present
# http://www.reactivated.net/writing_udev_rules.html#strsubst
# http://webapp5.rrz.uni-hamburg.de/SuSe-Dokumentation/manual/sles-admin_de/manual/sec.udev.rules.html
#
# udevadm info -a -p $(udevadm info -q path -n /dev/sdX)
# udevadm info -a --name=/dev/sdX
# udevadm control --reload-rules
# udevadm test /sys/block/sdX
# udevadm monitor --environment

set -e

. /etc/default/openmediavault
. /usr/share/openmediavault/scripts/helper-functions

OMV_USBBACKUP_UDEV_RULE_CONFIG=${OMV_USBBACKUP_UDEV_RULE_CONFIG:-"/etc/udev/rules.d/99-openmediavault-usbbackup.rules"}
OMV_USBBACKUP_SCRIPTS_DIR=${OMV_USBBACKUP_SCRIPTS_DIR:-"/var/lib/openmediavault/usbbackup.d"}
OMV_USBBACKUP_SCRIPTS_MASK=${OMV_USBBACKUP_SCRIPTS_MASK:-"755"}
OMV_USBBACKUP_LOGFILE=${OMV_USBBACKUP_LOGFILE:-"/var/log/rsync.log"}

# Create the backup jobs. Each job is configured in a seperate script.
mkdir -p ${OMV_USBBACKUP_SCRIPTS_DIR}
rm -f ${OMV_USBBACKUP_SCRIPTS_DIR}/rsync-*
rm -f ${OMV_USBBACKUP_SCRIPTS_DIR}/sync-*

# Create the scripts regardless if enabled or disabled.
xmlstarlet sel -t -m "//services/usbbackup/jobs/job" \
  -v "uuid" \
  -i "position() != last()" -n -b \
  ${OMV_CONFIG_FILE} | xmlstarlet unesc |
  while read uuid; do
	  filename="${OMV_USBBACKUP_SCRIPTS_DIR}/rsync-${uuid}"
	  runfile="/var/run/rsync-${uuid}"

	  xmlstarlet sel -t -m "//services/usbbackup/jobs/job[uuid='${uuid}']" \
		-o "#!/bin/sh" -n \
		-o "# This configuration file is auto-generated." -n \
		-o "# WARNING: Do not edit this file, your changes will be lost." -n \
		-o ". /usr/share/openmediavault/scripts/helper-functions" -n \
		-o "cleanup() {" -n \
		-o "  omv_kill_children \$\$" -n \
		-o "  rm -f ${runfile}" -n \
		-o "  exit" -n \
		-o "}" -n \
		-o "[ -e ${runfile} ] && exit 1" -n \
		-v "concat('if ! omv_is_mounted \"${OMV_MOUNT_DIR}/',fsuuid,'\" ; then')" -n \
		-o "    echo \"External storage device not mounted!\"" -n \
		-o "    exit 1" -n \
		-o "fi" -n \
		-o "trap cleanup 0 1 2 5 15" -n \
		-o "touch ${runfile}" -n \
		-i "mode = 'push' and usesubdir = '1'" \
		  -o "# Create target directory on external storage device." -n \
		  -v "concat('mkdir -p \"${OMV_MOUNT_DIR}/',fsuuid,'/')" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_NAME} -o "\"" -n \
		  -v "concat('chmod 777 \"${OMV_MOUNT_DIR}/',fsuuid,'/')" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_NAME} -o "\"" -n \
		-b \
		-o "# Synchronise directories ..." -n \
		-o "echo \"Please wait, syncing <" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_PATH} -v "concat('> to <${OMV_MOUNT_DIR}/',fsuuid)" -i "usesubdir = '1'" -o "/" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_NAME} -b -o "> ...\n\"" -n \
		-o "rsync --verbose --log-file=\"${OMV_USBBACKUP_LOGFILE}\"" \
		-i "recursive = '1'" -o " --recursive" -b \
		-i "times = '1'" -o " --times" -b \
		-i "compress = '1'" -o " --compress" -b \
		-i "archive = '1'" -o " --archive" -b \
		-i "delete = '1'" -o " --delete" -b \
		-i "quiet = '1'" -o " --quiet" -b \
		-i "perms = '1'" -o " --perms" -b \
		-i "acls = '1'" -o " --acls" -b \
		-i "xattrs = '1'" -o " --xattrs" -b \
		-i "partial = '1'" -o " --partial" -b \
		-i "string-length(extraoptions) > 0" -v "concat(' ',extraoptions)" -b \
		-i "mode = 'push'" -o " \"" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_PATH} -o "/\"" -v "concat(' \"${OMV_MOUNT_DIR}/',fsuuid)" -i "usesubdir = '1'" -o "/" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_NAME} -b -o "\"" -b \
		-i "mode = 'pull'" -v "concat(' \"${OMV_MOUNT_DIR}/',fsuuid)" -i "usesubdir = '1'" -o "/" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_NAME} -b -o "/\" \"" ${OMV_XMLSTARLET_GET_SHAREDFOLDER_PATH} -o "\"" -b \
		-o " & wait \$!" \
		${OMV_CONFIG_FILE} | xmlstarlet unesc > ${filename}
	  chmod ${OMV_USBBACKUP_SCRIPTS_MASK} ${filename}
  done

# Create the scripts executed by udev when the USB storage device is connected.
# Process duplicate jobs per filesystem only once (emulate SQL 'DISTINCT' to
# do this).
xmlstarlet sel -t -m "//services/usbbackup/jobs/job[not(fsuuid=preceding-sibling::*/fsuuid)]" \
  -v "fsuuid" \
  -i "position() != last()" -n -b \
  ${OMV_CONFIG_FILE} | xmlstarlet unesc |
  while read fsuuid; do
	  filename="${OMV_USBBACKUP_SCRIPTS_DIR}/sync-${fsuuid}"
	  runfile="/var/run/sync-${fsuuid}"

	  xmlstarlet sel -t \
		-o "#!/bin/sh" -n \
		-o "# This configuration file is auto-generated." -n \
		-o "# WARNING: Do not edit this file, your changes will be lost." -n \
		-o ". /usr/share/openmediavault/scripts/helper-functions" -n \
		-o "cleanup() {" -n \
		-o "  omv_kill_children \$\$" -n \
		-o "  rm -f ${runfile}" -n \
		-o "  exit" -n \
		-o "}" -n \
		-o "[ -e ${runfile} ] && exit 1" -n \
		-o "if [ ! -e \"/dev/disk/by-uuid/${fsuuid}\" ]; then" -n \
		-o "    echo \"External storage device not found!\"" -n \
		-o "    omv_beep_error" -n \
		-o "    exit 1" -n \
		-o "fi" -n \
		-o "if ! omv_is_mounted \"${OMV_MOUNT_DIR}/${fsuuid}\" ; then" -n \
		-o "    echo \"External storage device not mounted!\"" -n \
		-o "    omv_beep_error" -n \
		-o "    exit 1" -n \
		-o "fi" -n \
		-o "trap cleanup 0 1 2 5 15" -n \
		-o "touch ${runfile}" -n \
		-o "omv_beep_start" -n \
		-m "//services/usbbackup/jobs/job[fsuuid='${fsuuid}' and enable='1']" \
		  -v "concat('${OMV_USBBACKUP_SCRIPTS_DIR}/rsync-',uuid,' & wait \$!')" -n \
		-b \
		-o "sync" -n \
		-o "umount \"${OMV_MOUNT_DIR}/${fsuuid}\"" -n \
		-o "omv_beep_ok" \
		${OMV_CONFIG_FILE} | xmlstarlet unesc > ${filename}
	  chmod ${OMV_USBBACKUP_SCRIPTS_MASK} ${filename}
  done

# Create udev rules file.
xmlstarlet sel -t \
  -o "# Start at sdb to ignore the system hard drive" -n \
  -o "KERNEL!=\"sd[b-z]*\", SUBSYSTEM!=\"block\", GOTO=\"omv_automount_usb_end\"" -n \
  -o "# Only process USB/eSATA devices" -n \
  -o "ENV{ID_BUS}!=\"usb|ata\", GOTO=\"omv_automount_usb_end\"" -n \
  -o "# Skip all filesystems that does not match the given ID_FS_UUID" -n \
  -m "//services/usbbackup/jobs/job[not(fsuuid=preceding-sibling::*/fsuuid)]" \
	-i "count(../*[enable='1']) > 0" \
	  -v "concat('ENV{ID_FS_UUID}==\"',fsuuid,'\", GOTO=\"omv_automount_usb_mkdir\"')" -n \
	-b \
  -b \
  -o "GOTO=\"omv_automount_usb_end\"" -n \
  -o "# Create the mount directory in ${OMV_MOUNT_DIR}" -n \
  -o "LABEL=\"omv_automount_usb_mkdir\"" -n \
  -o "ACTION==\"add\", RUN+=\"/bin/mkdir -p ${OMV_MOUNT_DIR}/%E{ID_FS_UUID}\"" -n \
  -o "# Global mount options" -n \
  -o "ACTION==\"add\", ENV{omv_mount_options}=\"defaults\"" -n \
  -o "# Filesystem-specific mount options" -n \
  -o "ACTION==\"add\", ENV{ID_FS_TYPE}==\"ext3|ext4\", ENV{omv_mount_options}=\"%E{omv_mount_options},acl,user_xattr,noexec\"" -n \
  -o "ACTION==\"add\", ENV{ID_FS_TYPE}==\"jfs|xfs\", ENV{omv_mount_options}=\"%E{omv_mount_options},noexec\"" -n \
  -o "ACTION==\"add\", ENV{ID_FS_TYPE}==\"hfsplus\", ENV{omv_mount_options}=\"%E{omv_mount_options},force\"" -n \
  -o "# Automount ntfs filesystems using ntfs-3g driver" -n \
  -o "ACTION==\"add\", ENV{ID_FS_TYPE}==\"ntfs\", RUN+=\"/bin/mount -t ntfs-3g -o %E{omv_mount_options} /dev/%k ${OMV_MOUNT_DIR}/%E{ID_FS_UUID}\"" -n \
  -o "# Automount all other filesystems" -n \
  -o "ACTION==\"add\", ENV{ID_FS_TYPE}!=\"ntfs\", RUN+=\"/bin/mount -t auto -o %E{omv_mount_options} /dev/%k ${OMV_MOUNT_DIR}/%E{ID_FS_UUID}\"" -n \
  -o "# Execute rsync command" -n \
  -m "//services/usbbackup/jobs/job[not(fsuuid=preceding-sibling::*/fsuuid)]" \
	-i "count(../*[enable='1']) > 0" \
	  -v "concat('ACTION==\"add\", ENV{ID_FS_UUID}==\"',fsuuid,'\", RUN+=\"/usr/sbin/omv-run')" \
	  -i "sendemail[. = '1']" \
		-o " -e -s 'USB Backup (device=" -v fsuuid -o ")" \
		-i "string-length(comment) > 0" \
		  -v "concat(' - ',translate(comment,'\

',' '))" \
		-b \
		-o "' -a 'From: USB Backup <root>'" \
	  -b \
	  -v "concat(' ${OMV_USBBACKUP_SCRIPTS_DIR}/sync-',fsuuid,'\"')" -n \
	-b \
  -b \
  -o "# Cleanup after device removal" -n \
  -o "ACTION==\"remove\", ENV{ID_FS_UUID}!=\"\", RUN+=\"/bin/umount -l ${OMV_MOUNT_DIR}/%E{ID_FS_UUID}\", RUN+=\"/bin/rmdir ${OMV_MOUNT_DIR}/%E{ID_FS_UUID}\"" -n \
  -o "# Exit" -n \
  -o "LABEL=\"omv_automount_usb_end\"" \
  ${OMV_CONFIG_FILE} | xmlstarlet unesc > ${OMV_USBBACKUP_UDEV_RULE_CONFIG}
