#!/bin/bash SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) cd $SCRIPT_DIR . ./backup.config function log() { echo -e "\e[96m`date +'%d/%m/%Y %H:%M:%S'`\e[39m $1" echo -e "`date +'%d/%m/%Y %H:%M:%S'` $1" >> $LOG } function usage() { echo echo -e "./backup.sh \e[93m--help \e[39m" echo -e "./backup.sh \e[93m--mount \e[39m" echo -e "./backup.sh \e[93m--umount \e[39m" echo -e "./backup.sh \e[93m--format=\e[39m/dev/sda" echo echo "Local directories" echo -e "./backup.sh \e[92m--src=\e[39m/mnt/data \e[96m--dst=\e[39m/backup" echo -e "./backup.sh \e[92m--src=\e[39m/mnt/vmachines/storage \e[96m--dst=\e[39m/backup/data/VMachines" echo -e "./backup.sh \e[92m--src=\e[39m/mnt/projects \e[96m--dst=\e[39m/backup \e[91m--delete=\e[39myes" echo echo "Remote directories [smb|ssh]" echo -e "./backup.sh \e[35m--config=\e[39m./windows.backup" echo echo -e " $1" echo exit } function another_instance() { if [ $(ps | grep backup.sh) ];then log "[FAIL] another instance is running" return 1 fi return 0 } function mount_smb() { if mkdir /mnt/${PID}; then log "[ OK ] creating /mnt/${PID}" if mount -t cifs //${HOST}/${SHARE} /mnt/${PID} -o username=${USER},password=${PASS}; then log "[ OK ] mount //${HOST}/${SHARE} to /mnt/${PID}" return 0 else log "[FAIL] mount //${HOST}/${SHARE} to /mnt/${PID}" return 1 fi else log "[FAIL] to create /mnt/${PID}" return 1 fi } function mount_ssh() { if mkdir /mnt/${PID}; then if ssh -q -o BatchMode=yes -o ConnectTimeout=10 ${USER}@${HOST} exit; then if sshfs -o ro ${USER}@${HOST}:${SHARE} /mnt/${PID}; then return 0 else log "[FAIL] mount ${USER}@${HOST}:${SHARE} to /mnt/${SRC}" return 1 fi else log "[FAIL] ssh ${USER}@${HOST}" return 1 fi else log "[FAIL] to create /mnt/${PID}" return 1 fi } function get_luks_password() { LUKS_PASS=$(curl --connect-timeout 10 -s ${LUKS_PASS_URL} | jq -r ".password") if [ -z "${LUKS_PASS}" ]; then log "[FAIL] to get LUKS password" return 1 else log "[ OK ] getting LUKS password" return 0 fi } function find_luks() { for DEV in `ls -1 /dev/disk/by-id/usb*`; do if /sbin/cryptsetup isLuks ${DEV}; then USB_DEV=${DEV} LOGDEV=$(echo $DEV | cut -c21- ) log "[ OK ] find encrypted ${LOGDEV}" return 0 fi done log "[FAIL] to find encrypted disk" return 1 } function mount_luks2() { for n in `seq 0 9`; do waiting=$(grep 'Dirty\|Writeback' /proc/meminfo | grep Writeback: | awk {'print $2'}) if [[ wating -eq "0" ]]; then for n in `seq 0 9`; do echo $1 | /sbin/cryptsetup luksOpen $2 crypted_usb if [ $? = 0 ]; then log "[ OK ] luksOpen crypted_usb" return 0 fi sleep 60 done log "[FAIL] luksOpen crypted_usb (error)" return 1 fi sleep 60 done log "[FAIL] luksOpen crypted_usb (timeout)" return 1 } function mount_luks() { if [[ -L /dev/mapper/crypted_usb ]]; then log "[ !! ] founbd old crypted_usb" dmsetup remove /dev/mapper/crypted_usb if [ $? = 0 ]; then log "[ !! ] old crypted_usb removed" fi fi waiting=$(grep 'Dirty\|Writeback' /proc/meminfo | grep Writeback: | awk {'print $2'}) if [[ wating -eq "0" ]]; then echo $1 | /sbin/cryptsetup luksOpen $2 crypted_usb if [ $? = 0 ]; then log "[ OK ] luksOpen crypted_usb" return 0 fi fi } function mount_usb() { if mount /dev/mapper/crypted_usb /mnt/usb; then log "[ OK ] mount crypted_usb to /mnt/usb" return 0 else log "[FAIL] to mount crypted_usb to /mnt/usb" return 1 fi } function check_space () { src_size=$(du -s /mnt/${PID} | awk '{print $1}') dst_size=$(df /mnt/usb | grep "^/" | awk {'print $4'}) log "USB usage size: $src_size" log "USB available size: $dst_size" if [ "$dst_size" -gt "$src_size" ]; then log "[ OK ] enought available space" return 0 else log "[FAIL] not enought available space" return 1 fi } function sync_files() { SRC="/mnt/${PID}${SOURCE}" DST="/mnt/usb${DESTINATION}" log "SOURCE : ${SRC}" log "DESTINATION : ${DST}" log "DELETE : ${DELETE:-"No (default)"}" log "------------- RSYNC STARTED ---------" cd $SRC_PATH if [ "$DELETE" == "yes" ]; then rsync -av --stats \ --temp-dir=/tmp \ --links \ --human-readable \ --no-owner \ --no-group \ --include ".*" \ --delete \ "${SRC}" "${DST}" | sed '/sending\ incremental\ file\ list/d' | tee -a $LOG else rsync -av \ --stats \ --temp-dir=/tmp \ --include ".*" \ --links \ --human-readable \ --no-owner \ --no-group \ "${SRC}" "${DST}" | sed '/sending\ incremental\ file\ list/d' | tee -a $LOG fi cd - > /dev/null log "------------- RSYNC ENDED -----------" return 0 } function check_copy () { cd ${SOURCE} find * -type f -exec ls -s {} \; > /root/backup-src.log cd ${DST_DIR}/${SRC_DIR} find * -type f -exec ls -s {} \; > /root/backup-dst.log cd if diff -r /root/backup-src.log /root/backup-dst.log; then log "[ OK ] files check" else log "[FAIL] files check" fi return 0 } function umount_usb() { if umount /mnt/usb; then log "[ OK ] umount /mnt/usb" return 0 else log "[FAIL] umount /mnt/usb" return 1 fi } function luks_close() { for n in `seq 1 12`; do cryptsetup close crypted_usb if [ $? = 0 ]; then log "[ OK ] luksClose /dev/mapper/crypted_usb" return 0 fi sleep 10 done log "[FAIL] luksClose (timeout)" return 1 } function statistics_usb() { USBTOTAL=$(df -h | grep /mnt/usb | awk '{print $2}') USBUSAGE=$(df -h | grep /mnt/usb | awk '{print $5}') USBUSED=$(df -h | grep /mnt/usb | awk '{print $3}') USBFREE=$(df -h | grep /mnt/usb | awk '{print $4}') log "USB DISK Total : ${USBTOTAL}" log "USB DISK Usage : ${USBUSAGE}" log "USB DISK Used : ${USBUSED}" log "USB DISK Free : ${USBFREE}" } function debug_mount() { if get_luks_password; then if find_luks; then if mount_luks $LUKS_PASS $USB_DEV; then if mount_usb; then ls -l /mnt/usb fi fi fi fi exit } function send_mail() { COPIED=$( cat ${LOG} | grep "Number of created files:" | awk {'print $5'} ) DELETED=$( cat ${LOG} | grep "Number of deleted files:" | awk {'print $5'} ) TRANSFERRED=$( cat ${LOG} | grep "Number of regular files transferred:" | awk {'print $6'} ) SUBJECT="${COPIED} files copied, ${DELETED} files deleted, ${TRANSFERRED} files transferred" echo "Backup Report" | mutt -s $SUBJECT -F /etc/muttrc $MAIL_RECIPIENT -a $LOG } function free_to_run() { for n in `seq 0 9`; do if [[ `ps aux | grep backup.sh | grep -v grep` ]]; then return 0 fi sleep 120 done log "[ ] wating another instance to finish" return 1 } function umount_remote() { if umount /mnt/$PID; then log "[ OK ] umount /mnt/${PID}" if rmdir /mnt/$PID; then log "[ OK ] rmdir /mnt/${PID}" else log "[FAIL] rmdir /mnt/${PID}" fi else log "[FAIL] umount /mnt/${PID}" fi } function format() { if [ -b ${FORMAT} ]; then if cryptsetup -y -v luksFormat ${FORMAT}; then log "[ OK ] formating ${FORMAT}" else log "[FAIL] formating ${FORMAT}" fi else log "[FAIL] ${FORMAT} not found" fi exit } function main () { if another_instance; then if get_luks_password; then if find_luks; then if mount_luks $LUKS_PASS $USB_DEV; then if mount_usb; then sync_files statistics_usb if umount_usb; then luks_close fi fi fi fi fi fi send_mail } for i in "$@" do case $i in --help) usage ;; --mount) debug_mount ;; --umount) umount_usb && luks_close ;; --format=*) FORMAT="${i#*=}" format shift ;; --src=*) SRC="${i#*=}" shift ;; --dst=*) DST="${i#*=}" shift ;; --delete=*) DELETE="${i#*=}" shift ;; --config=*) CONFIG="${i#*=}" shift ;; *) usage ;; esac done if [[ -n "$CONFIG" ]]; then PID=$$ source $CONFIG [ -z "$PROTO" ] && usage "config \e[92m'PROTO'\e[39m is missing" [ -z "$SHARE" ] && usage "config \e[92m'SHARE'\e[39m is missing" [ -z "$SOURCE" ] && usage "config \e[92m'SOURCE'\e[39m is missing" [ -z "$DESTINATION" ] && usage "config \e[92m'DESTINATION'\e[39m is missing" if [ "$PROTO" = "smb" ]; then [ -z "$USER" ] && usage "config \e[92m'USER'\e[39m is missing" [ -z "$PASS" ] && usage "config \e[92m'PASS'\e[39m is missing" if mount_smb; then main fi fi if [ "$PROTO" = "ssh" ]; then if mount_ssh; then main fi fi umount_remote fi exit [ -z "$SOURCE" ] && usage "option \e[92m'--src'\e[39m is missing" [ -z "$DESTINATION" ] && usage "option \e[92m'--dst'\e[39m is missing" main