Last active
August 9, 2024 21:57
-
-
Save pythoninthegrass/03cf6d81ef26b0e6bfac81b49c63bba1 to your computer and use it in GitHub Desktop.
rsync backup script with logging and crontab usage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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 |
Author
pythoninthegrass
commented
Aug 9, 2024
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment