Skip to content

Instantly share code, notes, and snippets.

@pythoninthegrass
Last active August 9, 2024 21:57
Show Gist options
  • Save pythoninthegrass/03cf6d81ef26b0e6bfac81b49c63bba1 to your computer and use it in GitHub Desktop.
Save pythoninthegrass/03cf6d81ef26b0e6bfac81b49c63bba1 to your computer and use it in GitHub Desktop.
rsync backup script with logging and crontab usage
#!/usr/bin/env bash
set -euo pipefail
# trap exit signals 2, 1, 15
trap "exit" SIGINT SIGHUP SIGTERM
cat << 'DESCRIPTION' >/dev/null
USAGE:
backup.sh [SRC] [DEST] [EXT] [TLD] [SUB_DIR] [LOG_PATH]
EXAMPLES:
Pass all arguments
backup.sh /etc/openvpn /nfsshare/vpn .ovpn openvpn client_configs /tmp
Pass single argument
SRC="/etc/openvpn" backup.sh # inline
export SRC="/etc/openvpn" # export
backup.sh
Run with defaults
backup.sh
Cron job
0 0 * * * LOG_PATH=/var/log /usr/local/bin/backup.sh
DESCRIPTION
# TODO: use getopts for argument parsing
# postional parameters -> env vars -> defaults
SRC="${1:-${SRC:-/etc/openvpn}}"
DEST="${2:-${DEST:-/nfsshare/vpn}}"
EXT="${3:-${EXT:-.ovpn}}"
TLD="${4:-${TLD:-openvpn}}"
SUB_DIR="${5:-${SUB_DIR:-client_configs}}"
_LOG_PATH="${6:-${LOG_PATH:-/tmp}}"
# $USER
[[ -n $(logname 2>/dev/null) ]] && logged_in_user=$(logname) || logged_in_user=$(whoami)
# $HOME
logged_in_home=$(eval echo "~${logged_in_user}")
# logging
log_name=$(basename "$0")
log_path="${_LOG_PATH}"
log_file="${log_path}/${log_name%.sh}.log"
pid=$$
start_time=0
# Log a message with timestamp and script info
# Arguments:
# $1 - The message to log
logger() {
local timestamp
timestamp=$(date "+%a %b %d %H:%M:%S")
echo "${timestamp} ${log_name}[${pid}]: $1" | tee -a "${log_file}"
}
# Print timestamp information
# Arguments:
# $1 - Either "start" or "end"
timestamp() {
local current_time
local end_time
local duration
local minutes
local seconds
local milliseconds
current_time=$(date +%s%3N) # Get time in milliseconds
if [[ $1 == "start" ]]; then
start_time=$current_time
logger "Start time of script: $(date +%H:%M:%S.%3N)"
elif [[ $1 == "end" ]]; then
end_time=$current_time
logger "End time of script: $(date +%H:%M:%S.%3N)"
# Calculate duration in milliseconds
duration=$((end_time - start_time))
minutes=$((duration / 60000))
seconds=$(( (duration % 60000) / 1000 ))
milliseconds=$((duration % 1000))
if [[ $minutes -gt 0 ]]; then
logger "Script completed in ${minutes}m ${seconds}s ${milliseconds}ms."
elif [[ $seconds -gt 0 ]]; then
logger "Script completed in ${seconds}s ${milliseconds}ms."
else
logger "Script completed in ${milliseconds}ms."
fi
fi
}
# Validate sudo access
# Returns:
# 0 if sudo access is valid, 1 otherwise
valid_sudo() {
if ! sudo -v; then
logger "Script must be run as root. Please re-run with admin credentials. Exiting..."
return 1
fi
}
# Check if a directory exists and is writable
# Arguments:
# $1 - The directory to check
# Returns:
# 0 if directory exists and is writable, 1 otherwise
check_dir() {
local dir
dir="$1"
if [[ ! -d "${dir}" ]]; then
logger "Warn: Destination directory ${dir} does not exist. Attempting to create..."
if ! sudo mkdir -p "${dir}"; then
logger "Error: Failed to create directory ${dir}. Check permissions."
return 1
fi
logger "Successfully created directory ${dir}"
fi
if [[ ! -w "${dir}" ]]; then
logger "Error: Destination directory ${dir} is not writable."
return 1
fi
return 0
}
# Perform the backup using rsync
# Returns:
# 0 if backup is successful, 1 otherwise
perform_backup() {
local ext
local src
local dest
local tld_name
local sub_dir_name
local tld_dir
local sub_dir
local rsync_cmd
local rsync_output
ext="$EXT"
src="${SRC%/}" # Remove trailing slash if present
dest="${DEST%/}" # Remove trailing slash if present
tld_name="${TLD}"
sub_dir_name="${SUB_DIR}"
tld_dir="${dest}/${tld_name}"
sub_dir="${dest}/${sub_dir_name}"
logger "Using destination: ${dest}"
if ! check_dir "${dest}" || ! check_dir "${sub_dir}" || ! check_dir "${tld_dir}"; then
return 1
fi
logger "Starting backup"
# Backup top-level directory
logger "Backing up: ${src}"
rsync_cmd=(rsync -arvW --progress --stats --ignore-existing)
if ! rsync_output=$("${rsync_cmd[@]}" "${src}/" "${tld_dir}/" 2>&1); then
logger "Error: Rsync backup failed for ${src}"
logger "Rsync output: ${rsync_output}"
return 1
fi
# Backup ${src}/*.${ext} files
logger "Backing up: ${logged_in_home}/*${ext}"
rsync_cmd=(rsync -arvW --progress --stats --ignore-existing --include="*${ext}" --exclude='*')
if ! rsync_output=$("${rsync_cmd[@]}" "${logged_in_home}/" "${sub_dir}/" 2>&1); then
logger "Error: Rsync backup failed for ${logged_in_home}/*${ext}"
logger "Rsync output: ${rsync_output}"
return 1
fi
logger "Backup completed successfully."
return 0
}
main() {
if ! valid_sudo; then exit 1; fi
timestamp "start"
if ! perform_backup; then
logger "Backup process encountered errors."
timestamp "end"
exit 1
fi
timestamp "end"
}
main "$@"
exit 0
@pythoninthegrass
Copy link
Author

# clone
git clone git@gist.github.com:03cf6d81ef26b0e6bfac81b49c63bba1.git rsync_backup

# symlink
sudo ln -s $(pwd)/backup.sh /usr/local/bin/backup

# cron job
$ sudo crontab -e
0 0 * * * LOG_PATH=/var/log /usr/local/bin/backup

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment