#!/bin/bash # ______ ___ _____ _ ___ _______ # | ___ \/ _ \/ __ \| | / / | | | ___ \ # | |_/ / /_\ \ / \/| |/ /| | | | |_/ / # | __/| _ | | | \| | | | __/ # | | | | | | \__/\| |\ \ |_| | | # \_| \_| |_/\____/\_| \_/\___/\_| # LIGHTWEIGHT BACKUP SCRIPT # https://github.com/Pendrag00n/Packup # ShellCheck Standards Compliant #/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # # CONFIGURATION # (Modify accordingly) # General variables: backuppath="/var/packup" # Path where the backup will be stored. //server/share for SMB remote backups. Absolute path for SSH remote backups logpath="/home/julio" # Path to the log file logfile="packup.log" # Name of the log file files=(/etc/ /bin/ /home/julio/Videos/) # Enter files/folders separated by a space backuppermission="0600" # Permission of the backup file (Use 4 digits) restartservices="" # "" To disable. Stops these services before the backup and restarts them after the backup. (Use space to separate services) # Incremental backup variables: incremental="true" # If set to true, the backup will be incremental and will use rsync instead of tar # Send email when something goes wrong: (Make sure you have correctly set up a MTA on your system. Ex: https://www.tutorialspoint.com/configure-postfix-to-use-gmail-smtp-on-ubuntu) sendemail="false" # If set to true, an email will be sent when something goes wrong destination="receiver@example.com" # Email address where the email will be sent subject="BACKUP FAILED!" # Subject of the email sendonsuccess="false" # If set to true, an email will be sent when the backup is finished # Remote backup variables: remotebackup="true" # If set to true, the backup will be stored on a remote location method="rsync" # (rsync or smb) Method used to backup to a remote location port="22" # Default SMB/CIFS port is 445. Default SSH port is 22 SMBmountpath="/home/$SUDO_USER/packuptmp" # This variable is only used if backup up to a SMB remote location SMBunmountwhenfinished="true" # If set to true, the remote path will be unmounted when the backup is finished SSHusername="julio" # Username used to connect to the remote server SSHip="192.168.117.137" # IP address of the remote server # Delete old backups: deleteoldbackups="false" # If set to true, old backups will be deleted olderthan="90" # (Expressed in days) If $deleteoldbackups is set to true, this variable will be used to determine how old the backups should be before they are deleted #/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\ # Variables used for formatting the time and date: filetail=$(date +%d-%m-%Y_%H-%M-%S) logdate=$(date +%d-%m-%Y) # Other variables: BASEDIR=$(dirname $0) # Path to the directory where the script is located # check if script is being ran as sudo if [ "$EUID" -ne 0 ]; then echo "ERROR: Please run as root" >>"$logpath"/$logfile echo "ERROR: Please run as root" if [ $sendemail = "true" ]; then echo "Backup has failed! (Please run as root) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 1 fi # Test if the log path and log file exists if [ ! -d "$logpath" ]; then mkdir -p "$logpath" fi if [ ! -f "$logpath"/$logfile ]; then touch "$logpath"/$logfile fi # Test if boolean variables are set to true or false if [ "$incremental" = "true" ]; then filetail="inc" elif ! [ "$incremental" = "false" ]; then echo "ERROR: $incremental is not a valid value for incremental (true/false). Exiting script..." >>"$logpath"/$logfile echo "ERROR: $incremental is not a valid value for incremental (true/false). Exiting script..." if [ $sendemail = "true" ]; then echo "Backup has failed! ($incremental is not a valid value for incremental) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi # if $SMBunmountwhenfinished is something else than true or false then set it to false if ! [ "$SMBunmountwhenfinished" = "true" ] && ! [ "$SMBunmountwhenfinished" = "false" ]; then SMBunmountwhenfinished="false" fi # If $sendonsuccess is something else than true or false then set it to false if ! [ "$sendonsuccess" = "true" ] && ! [ "$sendonsuccess" = "false" ]; then sendonsuccess="false" fi # If $backuppath starts with // then set $method to smb if [ ${backuppath:0:2} = "//" ]; then method="smb" fi # If $backuppath ends with a /, remove it. if [ ${backuppath: -1} = "/" ]; then backuppath=${backuppath::-1} fi # If $SMBmountpath ends with a /, remove it if [ ${SMBmountpath: -1} = "/" ]; then SMBmountpath=${SMBmountpath::-1} fi # If $logpath ends with a /, remove it if [ ${logpath: -1} = "/" ]; then logpath=${logpath::-1} fi # Check if the files exist for file in "${files[@]}"; do if ! test -e "$file"; then echo "ERROR: $file can't be located. Exiting script" >>"$logpath"/$logfile echo "ERROR: $file can't be located. Exiting script" if [ $sendemail = "true" ]; then echo "Backup has failed! ($file can't be located) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi done # Check if $deleteoldbackups is a number greater than 1 if [ "$deleteoldbackups" = "true" ] && ! [[ $olderthan =~ ^[0-9]+$ ]] && test $olderthan -le 1; then echo "ERROR: $olderthan is not a valid number. Exiting script..." >>"$logpath"/$logfile echo "ERROR: $olderthan is not a valid number. Exiting script..." if [ $sendemail = "true" ]; then echo "Backup has failed! ($olderthan is not a valid number) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi # Check system packet manager if which apt >/dev/null; then install="apt install -y" elif which yum >/dev/null; then install="yum install -y" elif which dnf >/dev/null; then install="dnf install -y" elif which zypper >/dev/null; then install="zypper install -y" elif which pacman >/dev/null; then install="pacman -A --noconfirm" elif which apk >/dev/null; then install="apk add --no-cache" elif which emerge >/dev/null; then install="emerge -y" else echo "ERROR: No packet manager found. Exiting script..." >>"$logpath"/$logfile echo "ERROR: No packet manager found. Exiting script..." if [ $sendemail = "true" ]; then echo "Backup has failed! (No packet manager found) Check $logpath/$logfile for the full log" | mail -s "$subject" "$destination" fi exit 2 fi # Check if tar is installed if ! which tar >/dev/null; then echo "tar was not installed and is being installed now" $install tar if ! $install tar; then echo "ERROR: tar could not be installed. Exiting script..." >>"$logpath"/$logfile echo "ERROR: tar could not be installed. Exiting script..." if [ $sendemail = "true" ]; then echo "Backup has failed! (tar could not be installed) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi echo "[ $logdate ]: tar was not installed and was automatically installed" >>"$logpath"/$logfile fi # Check if rsync is installed if [ "$remotebackup" = "true" ] && ! which rsync >/dev/null; then echo "rsync was not installed and is being installed now" $install rsync if ! $install rsync; then echo "ERROR: rsync could not be installed. Exiting script..." >>"$logpath"/$logfile echo "ERROR: rsync could not be installed. Exiting script..." if [ $sendemail = "true" ]; then echo "Backup has failed! (rsync could not be installed) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi echo "[ $logdate ]: rsync was not installed and was automatically installed" >>"$logpath"/$logfile fi # Test credentials file (ales.txt) and mount backup path if [ "$remotebackup" = "true" ]; then # Test if the IP provided has SSH/SMB running on the defined port if [ "$method" = "smb" ]; then IP=$(echo "$backuppath" | cut -d/ -f3) elif [ "$method" = "rsync" ]; then IP=$SSHip fi echo "Testing if SSH/SMB is up on port $port..." if echo "Q" | nc -w 3 "$IP" "$port" >/dev/null; then echo "SSH/SMB is up on $IP on port $port !" else echo "Error: SSH/SMB doesn't seem to be up on $IP on port $port :( Exiting script..." echo "Error: SSH/SMB doesn't seem to be up on $IP on port $port :( Exiting script..." >>"$logpath"/$logfile if [ $sendemail = "true" ]; then echo "Backup has failed! (SSH/SMB doesn't seem to be up on $IP port $port) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi # Test credentials file (ales.txt) and mount backup path if [ "$method" = "smb" ]; then if [ -f "${BASEDIR}"/ales.txt ]; then chown root:root "${BASEDIR}"/ales.txt chmod 0600 "${BASEDIR}"/ales.txt else touch ales.txt echo $'username=\npassword=' >"${BASEDIR}"/ales.txt chown root:root "${BASEDIR}"/ales.txt chmod 0600 "${BASEDIR}"/ales.txt echo "The credentials file (ales.txt) was not found, a template has been created in the script's dir, fill it :( Exiting script" echo "[ $logdate ]: The credentials file (ales.txt) was not found, a template has been created in the script's dir, fill it :( Exiting script" >>"$logpath"/$logfile exit 2 fi # Check if the SMBmountpath exists, if not, create it. if [ ! -d "$SMBmountpath" ]; then mkdir -p "$SMBmountpath" fi chown root:root "$SMBmountpath" chmod 0600 "$SMBmountpath" # Check if the backup path is already mounted if mount | grep -q "$backuppath .*$SMBmountpath"; then echo "Drive seems to be mounted to the SMBmountpath already!" else # Mount the network drive location into the SMBmountpath echo "Installing cifs-utils" apt install cifs-utils -y echo "Mounting remote path..." mount -t cifs "$backuppath" "$SMBmountpath" -o credentials="$PWD"/ales.txt &>"$logpath"/temp_mount_error.log # if temp_mount_error.log exists, change it's permissions to 0600 if [ -f "$logpath"/temp_mount_error.log ]; then chown root:root "$logpath"/temp_mount_error.log chmod 0600 "$logpath"/temp_mount_error.log fi sleep 1 # Should work without this line if ! mount -t cifs "$backuppath" "$SMBmountpath" -o credentials="$PWD"/ales.txt; then echo "The remote path could not be mounted :( Exiting script" { echo "[ $logdate ]: The remote path could not be mounted :( Exiting script" echo -n "Reasons for failure:" cat "$logpath"/temp_mount_error.log } >>"$logpath"/$logfile if [ $sendemail = "true" ]; then echo "Backup has failed! (Couldn't mount remote path) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi exit 2 fi # If temp_mount_error.log is not empty, delete it if [ -s "$logpath"/temp_mount_error.log ]; then rm "$logpath"/temp_mount_error.log fi fi fi fi if [ ! -d "$backuppath" ]; then mkdir -p "$backuppath" fi #If requested, stop the services, then do the correct type of backup and determine if it failed or not failed="false" if [ -n "$restartservices" ]; then systemctl stop "$restartservices" fi if [ "$remotebackup" = "true" ]; then if [ "$incremental" = "true" ]; then if [ $method = "rsync" ]; then lastbackuppath=$(ls -td $backuppath/packup_* | head -n 1) rsync -avz --link-dest="$lastbackuppath" "${files[@]}" $SSHusername@$SSHip:"$backuppath"/packup_inc"$filetail"/ 2>"$logpath"/temp_backups_error.log if ! rsync -avz --link-dest="$lastbackuppath" "${files[@]}" $SSHusername@$SSHip:"$backuppath"/packup_inc"$filetail"/; then failed="true" fi fi if [ $method = "smb" ]; then tar -czpf "$SMBmountpath"/packup_"$filetail".tgz -g "$backuppath"/packup-incremental-data "${files[@]}" &>"$logpath"/temp_backups_error.log if ! tar -czpf "$SMBmountpath"/packup_"$filetail".tgz "${files[@]}"; then failed="true" fi fi fi if [ "$incremental" = "false" ]; then if [ $method = "rsync" ]; then rsync -avz --relative "${files[@]}" $SSHusername@$SSHip:"$backuppath"/packup_"$filetail" 2>"$logpath"/temp_backups_error.log | tail -1 | cut -d " " -f 4 >"$logpath"/backup_size.log && rsize=$(cat backup_size.log) rm "$logpath"/backup_size.log if ! rsync -avz --relative "${files[@]}" $SSHusername@$SSHip:"$backuppath"/packup_"$filetail"; then failed="true" fi fi if [ $method = "smb" ]; then tar -czpf "$SMBmountpath"/packup_"$filetail".tgz "${files[@]}" &>"$logpath"/temp_backups_error.log if ! tar -czpf "$SMBmountpath"/packup_"$filetail".tgz "${files[@]}"; then failed="true" fi fi fi fi if [ "$remotebackup" = "false" ]; then if [ "$incremental" = "true" ]; then tar -czpf "$backuppath"/packup_inc_"$filetail".tgz -g "$backuppath"/packup-incremental-data "${files[@]}" &>"$logpath"/temp_backups_error.log if ! tar -czpf "$backuppath"/packup_inc_"$filetail".tgz -g "$backuppath"/packup-incremental-data "${files[@]}"; then failed="true" fi fi if [ "$incremental" = "false" ]; then tar -czpf "$backuppath"/packup_"$filetail".tgz "${files[@]}" &>"$logpath"/temp_backups_error.log if ! tar -czpf "$backuppath"/packup_"$filetail".tgz "${files[@]}"; then failed="true" fi fi fi if [ -n "$restartservices" ]; then systemctl start "$restartservices" fi # Give information about the backup success or failure, set correct permissions and clean up if [ "$failed" = "false" ] && [ "$remotebackup" = "false" ]; then chmod $backuppermission "$backuppath"/packup_"$filetail".tgz echo "packup_$filetail.tgz was created in $backuppath (Took $SECONDS seconds and weighs $(du -sh "$backuppath"/packup_"$filetail".tgz | awk '{print $1}'))" echo "[ $logdate ]: packup_$filetail.tgz was created in $backuppath (Took $SECONDS seconds and weighs $(du -sh "$backuppath"/packup_"$filetail".tgz | awk '{print $1}'))" >>"$logpath"/$logfile chown root:root "$backuppath"/packup_"$filetail".tgz chmod 600 "$backuppath"/packup_"$filetail".tgz fi if [ "$failed" = "false" ] && [ "$remotebackup" = "true" ]; then # Fix filesize (and permissions) echo "packup_$filetail.tgz was created in $backuppath (Took $SECONDS seconds and weighs $rsize)" echo "[ $logdate ]: packup_$filetail.tgz was created in $backuppath (Took $SECONDS seconds and weighs $rsize)" >>"$logpath"/$logfile fi if [ $remotebackup = "false" ] && [ "$incremental" = "true" ]; then chown root:root "$backuppath"/packup-incremental-data chmod 600 "$backuppath"/packup-incremental-data fi if [ "$sendonsuccess" = "true" ]; then if [ "$remotebackup" = "true" ]; then echo "Backup has finished successfully on $(date | cut -d " " -f 1-4)! Took $SECONDS seconds and weighs $rsize" | mail -s "Backup Finished!" "$destination" else echo "Backup has finished successfully on $(date | cut -d " " -f 1-4)! Took $SECONDS seconds and weighs $(du -sh "$backuppath"/packup_"$filetail".tgz | awk '{print $1}')) " | mail -s "Backup Finished!" "$destination" fi fi if [ "$failed" = "true" ]; then echo "Backup exited with errors and the zipfile was deleted (Compression failed) :(" echo "[ $logdate ]: Backup exited with errors and the tarfile was deleted (Compression failed) :(" >>"$logpath"/$logfile if [ $sendemail = "true" ]; then echo "Backup has failed! (Compression failed) Check $logpath/$logfile for the full log!" | mail -s "$subject" "$destination" fi if [ "$remotebackup" = "false" ]; then # Remove incomplete backup if [ -f "$backuppath"/packup_"$filetail".tgz ]; then rm -rf "$backuppath"/packup_"$filetail".tgz fi fi # Send tar errors to the log file if [ -s "$logpath"/temp_backups_error.log ]; then { echo "Encountered the following errors:" cat "$logpath"/temp_backups_error.log echo "" } >>"$logpath"/$logfile fi # Remove the temp error log after being appended to the main log file if [ -f "$logpath"/temp_backups_error.log ]; then rm -f "$logpath"/temp_backups_error.log fi fi # If the remote backup path is mounted, unmount it if [ "$remotebackup" = "true" ] && [ "$SMBunmountwhenfinished" = "true" ]; then if mount | grep -q "$backuppath .*$SMBmountpath"; then umount "$SMBmountpath" echo "Unmounted remote path successfully" elif ! umount "$SMBmountpath"; then echo "Unmounting remote path failed, please unmount it manually" echo "[ $logdate ]: Unmounting remote path failed, please unmount it manually" >>"$logpath"/$logfile else echo "Remote path seems to be unmounted already... Skipping unmounting" fi fi # If $deleteoldbackups is set to true, check for backups older than $olderthan days inside $backuppath and delete them if [ "$deleteoldbackups" = "true" ]; then echo "Deleting backups older than $olderthan days..." if [ -z "$TERM" ]; then echo "" echo "Are you sure?" echo press Y to continue, any other key to exit read -n 1 -r -p "" if [ "$REPLY" != "Y" ]; then echo "Nothing was deleted, exiting script..." exit 0 fi fi find "$backuppath" -type f -name "packup_*.tgz" -maxdepth 1 -mtime +$olderthan -delete fi exit 0