New updates and fail safe mechanism

main
Anthony 2024-11-13 02:15:11 +08:00
parent 2a3c7341f0
commit 301d5dceda
7 changed files with 486 additions and 98 deletions

View File

@ -14,6 +14,8 @@ targetNodes:
globals:
envName: "${env.name}"
scriptPath: "/home/litespeed/mb-backups"
logPath: "/home/litespeed/mb-backups/logs"
onInstall:
- checkAddons
@ -21,12 +23,46 @@ onInstall:
- importScripts
settings:
scheduleSettings:
submitUnchanged: true
fields:
- name: frequency
caption: Backup Frequency
type: list
values:
daily: Daily Backup
weekly: Weekly Backup
default: daily
- name: hour
caption: Backup Hour (0-23)
type: string
regex: "^([0-9]|1[0-9]|2[0-3])$"
regexText: Enter a valid hour (0-23)
default: "0"
- name: dayOfWeek
caption: Day of Week (0=Sunday)
type: list
values:
0: Sunday
1: Monday
2: Tuesday
3: Wednesday
4: Thursday
5: Friday
6: Saturday
default: "0"
showIf:
frequency: weekly
backupSettings:
submitUnchanged: true
fields:
- name: blabel
caption: Backup Label
type: string
restoreSettings:
submitUnchanged: true
fields:
@ -42,6 +78,26 @@ buttons:
successText: The backup process has been finished successfully.
menu:
- caption: Configure Auto Backup
action: configureAutoBackup
confirmText: Configure automated backup schedule?
loadingText: Setting up backup schedule...
successText: Backup schedule configured successfully
settings: scheduleSettings
title: Configure Automated Backup Schedule
submitButtonText: Save Schedule
- caption: Remove Auto Backup
action: removeAutoBackup
confirmText: Remove automated backup schedule?
loadingText: Removing backup schedule...
successText: Backup schedule removed successfully
- caption: View Schedule
action: viewBackupSchedule
confirmText: View current backup schedule?
successText: Current schedule retrieved successfully
- caption: Check Backup Repository
confirmText: Do you want to check and repair the backup repository?
loadingText: Checking and repairing backup repository...
@ -106,11 +162,6 @@ menu:
confirmText: Are you sure you want to view database backups?
successText: Database backups listed successfully.
- caption: Auto Backup Sched
action: checkBackupSched
confirmText: Check the schedules for automated backups?
successText: Backup Scheds.
onUninstall:
- removeScript
@ -129,6 +180,32 @@ onAfterClone:
backupCount: "5"
actions:
configureAutoBackup:
- cmd[cp]:
user: root
commands: |
if [ ! -f /etc/restic-password ]; then
echo "Error: Restic password file not found"
exit 1
fi
RESTIC_PWD=$(cat /etc/restic-password)
if [ "${settings.frequency}" = "daily" ]; then
bash ${globals.scriptPath}/manage_backup_schedule.sh add "0 ${settings.hour} * * *" "$RESTIC_PWD"
else
bash ${globals.scriptPath}/manage_backup_schedule.sh add "0 ${settings.hour} * * ${settings.dayOfWeek}" "$RESTIC_PWD"
fi
- return:
type: info
message: "${response.out}"
removeAutoBackup:
- cmd[cp]:
user: root
commands: bash /home/litespeed/mb-backups/manage_backup_schedule.sh remove
- return:
type: info
message: "${response.out}"
checkBackupRepo:
- cmd[cp]:
user: root
@ -209,10 +286,10 @@ actions:
type: info
message: "${response.out}"
checkBackupSched:
viewBackupSchedule:
- cmd[cp]:
user: root
commands: bash /home/jelastic/mb-backups/check_sched.sh
commands: bash /home/litespeed/mb-backups/check_sched.sh
- return:
type: info
message: "${response.out}"

View File

@ -1,76 +1,174 @@
#!/bin/bash
# Check if Restic password was supplied
if [ "$#" -ne 1 ]; then
echo "Usage: $0 RESTIC_PASSWORD"
exit 1
fi
# Enable error handling
set -e
trap 'echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup failed" >> "$LOG_DIR/backup_error.log"' ERR
# Load the backup logic functions
source /home/jelastic/mb-backups/backup-logic.sh
# Configuration
RESTIC_PASSWORD="$1"
password_file="/etc/restic-password"
APP_PATH='/var/www/webroot/ROOT'
WP_CONFIG="${APP_PATH}/wp-config.php"
backupPath='/mnt/backups'
LOG_DIR="/home/jelastic/mb-backups/logs"
DB_NAME=$(grep "define( 'DB_NAME'" $WP_CONFIG | cut -d "'" -f 4)
DB_USER=$(grep "define( 'DB_USER'" $WP_CONFIG | cut -d "'" -f 4)
DB_PASSWORD=$(grep "define( 'DB_PASSWORD'" $WP_CONFIG | cut -d "'" -f 4)
TEMP_DIR="/tmp/wp_backup_tmp"
MAX_BACKUP_SIZE=$((10 * 1024 * 1024 * 1024)) # 10GB
# Database configuration
DB_NAME=$(grep "define( 'DB_NAME'" "$WP_CONFIG" | cut -d "'" -f 4)
DB_USER=$(grep "define( 'DB_USER'" "$WP_CONFIG" | cut -d "'" -f 4)
DB_PASSWORD=$(grep "define( 'DB_PASSWORD'" "$WP_CONFIG" | cut -d "'" -f 4)
BACKUP_USER='wp_backup'
BACKUP_PASSWORD='gaQjveXl24Xo66w'
export RESTIC_REPOSITORY="$backupPath"
export RESTIC_PASSWORD
# Ensure log directory exists
# Ensure directories exist
mkdir -p "$LOG_DIR"
mkdir -p "$TEMP_DIR"
# Cleanup function
cleanup() {
local exit_code=$?
rm -rf "${TEMP_DIR}"/*
if [ $exit_code -ne 0 ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup failed with exit code $exit_code" | tee -a "$LOG_DIR/backup_error.log"
fi
}
trap cleanup EXIT
# Check backup size
check_backup_size() {
local path="$1"
local size=$(du -sb "$path" | cut -f1)
if [ "$size" -gt "$MAX_BACKUP_SIZE" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Warning: Backup size exceeds 10GB for $path" | tee -a "$LOG_DIR/backup_warning.log"
return 1
fi
return 0
}
# Verify backup
verify_backup() {
local tag="$1"
local latest_snapshot=$(restic snapshots --latest 1 --tag "$tag" --json | jq -r '.[0].id')
if [ -n "$latest_snapshot" ]; then
restic check --read-data "$latest_snapshot"
return $?
fi
return 1
}
# Initialize backup
if [ ! -f "$password_file" ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Password file not found at $password_file" | tee -a "$LOG_DIR/backup_error.log"
exit 1
fi
export RESTIC_PASSWORD=$(cat "$password_file")
export RESTIC_REPOSITORY="$backupPath"
# Check repository
check_backup_repo
if [ $? -ne 0 ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup repository check failed" | tee -a "$LOG_DIR/backup_error.log"
exit 1
fi
# Backup functions
backup_core_files() {
local log_file="${LOG_DIR}/backup_core_files_$(date +'%Y-%m-%d').log"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Core Files Backup" | tee -a "$log_file"
local excludePaths=("$APP_PATH/wp-content/uploads")
check_backup_size "$APP_PATH" || return 1
local excludePaths=("$APP_PATH/wp-content/uploads")
local excludeOptions=""
for path in "${excludePaths[@]}"; do
excludeOptions+="--exclude $path "
done
if restic backup $excludeOptions "$APP_PATH" --tag core_files --tag full_backup; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Core files backup completed successfully." | tee -a "$log_file"
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Core files backup failed." | tee -a "$log_file"
verify_backup "core_files" || return 1
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Core files backup completed successfully" | tee -a "$log_file"
return 0
fi
return 1
}
backup_media_themes() {
local log_file="${LOG_DIR}/backup_media_themes_$(date +'%Y-%m-%d').log"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Media and Themes Backup" | tee -a "$log_file"
local includePaths=("$APP_PATH/wp-content/uploads")
for path in "${includePaths[@]}"; do
check_backup_size "$path" || continue
if restic backup "$path" --tag media_themes --tag full_backup; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup completed successfully for $path." | tee -a "$log_file"
verify_backup "media_themes" || return 1
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup completed successfully for $path" | tee -a "$log_file"
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup failed for $path." | tee -a "$log_file"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup failed for $path" | tee -a "$log_file"
return 1
fi
done
}
backup_database() {
local log_file="${LOG_DIR}/backup_database_$(date +'%Y-%m-%d').log"
local temp_dump="${TEMP_DIR}/wp_backup_${DB_NAME}_$(date +'%Y%m%d').sql"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Database Backup" | tee -a "$log_file"
# Correctly pass the password to mysqldump
if mysqldump -u "$BACKUP_USER" --password="$BACKUP_PASSWORD" "$DB_NAME" | restic backup --stdin --tag wordpress_db --tag full_backup; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Database backup completed successfully." | tee -a "$log_file"
if ! mysqldump -u "$BACKUP_USER" --password="$BACKUP_PASSWORD" "$DB_NAME" > "$temp_dump" 2>> "$log_file"; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Database dump failed" | tee -a "$log_file"
return 1
fi
check_backup_size "$temp_dump" || return 1
if restic backup "$temp_dump" --tag wordpress_db --tag full_backup; then
verify_backup "wordpress_db" || return 1
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Database backup completed successfully" | tee -a "$log_file"
return 0
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Database backup failed." | tee -a "$log_file"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Database backup failed" | tee -a "$log_file"
return 1
fi
}
# Execute backup functions
backup_core_files
backup_media_themes
backup_database
cleanup_old_backups() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting backup rotation" | tee -a "$LOG_DIR/backup_rotation.log"
restic forget \
--keep-daily 7 \
--keep-weekly 4 \
--keep-monthly 3 \
--prune \
--tag full_backup \
2>> "$LOG_DIR/backup_rotation.log"
}
# Main execution
main() {
local start_time=$(date +%s)
local backup_date=$(date +'%Y-%m-%d_%H-%M-%S')
local main_log="${LOG_DIR}/full_backup_${backup_date}.log"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting full backup process" | tee -a "$main_log"
backup_core_files || exit 1
backup_media_themes || exit 1
backup_database || exit 1
cleanup_old_backups
local end_time=$(date +%s)
local duration=$((end_time - start_time))
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Full backup completed in $duration seconds" | tee -a "$main_log"
}
# Execute main function
main

View File

@ -1,5 +1,8 @@
#!/bin/bash
# Load the backup logic functions
source /home/jelastic/mb-backups/backup-logic.sh
# Check for required arguments
if [ $# -ne 2 ]; then
echo "Usage: $0 <RESTIC_PASSWORD> <ADDITIONAL_TAG>"
@ -13,6 +16,7 @@ ADDITIONAL_TAG="$2"
# Configuration
APP_PATH='/var/www/webroot/ROOT'
backupPath='/mnt/backups'
password_file="/etc/restic-password"
LOG_DIR="/home/jelastic/mb-backups/logs"
LOG_FILE="${LOG_DIR}/backup_core_files_$(date +'%Y-%m-%d').log"
excludePaths=(
@ -26,6 +30,26 @@ mkdir -p "$LOG_DIR"
export RESTIC_REPOSITORY="$backupPath"
export RESTIC_PASSWORD
# Verify that the password file exists and matches the supplied password
if [ ! -f "$password_file" ]; then
echo "ERROR: Password file not found at $password_file" | tee -a "$LOG_FILE"
exit 1
fi
# Load and verify password from the file
stored_password=$(cat "$password_file")
if [ "$stored_password" != "$RESTIC_PASSWORD" ]; then
echo "ERROR: Password mismatch. Aborting backup." | tee -a "$LOG_FILE"
exit 1
fi
# Check repository accessibility and integrity
check_backup_repo
if [ $? -ne 0 ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup repository check failed. Aborting backup." | tee -a "$LOG_FILE"
exit 1
fi
# Logging start
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Core Files Backup with tags: core_files, $ADDITIONAL_TAG" | tee -a "$LOG_FILE"
@ -40,8 +64,8 @@ if restic backup $excludeOptions "$APP_PATH" --tag core_files --tag "$ADDITIONAL
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Core files backup completed successfully." | tee -a "$LOG_FILE"
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Core files backup failed." | tee -a "$LOG_FILE"
exit 1
fi
# Logging end
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup process finished." | tee -a "$LOG_FILE"

View File

@ -1,5 +1,8 @@
#!/bin/bash
# Load backup logic functions
source /home/jelastic/mb-backups/backup-logic.sh
# Validate input parameters
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <RESTIC_PASSWORD> <ADDITIONAL_TAG>"
@ -14,6 +17,7 @@ ADDITIONAL_TAG="$2"
APP_PATH='/var/www/webroot/ROOT'
WP_CONFIG="${APP_PATH}/wp-config.php"
backupPath='/mnt/backups'
password_file="/etc/restic-password"
LOG_DIR="/home/jelastic/mb-backups/logs"
LOG_FILE="${LOG_DIR}/backup_database_$(date +'%Y-%m-%d').log"
DB_NAME=$(grep "define( 'DB_NAME'" $WP_CONFIG | cut -d "'" -f 4)
@ -35,11 +39,32 @@ export MYSQL_PWD=$BACKUP_PASSWORD # Use the backup user's password for mysqldum
# Logging start
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Database Backup with additional tag: $ADDITIONAL_TAG" | tee -a "$LOG_FILE"
# Perform backup with additional tag
# Verify that the password file exists and matches the supplied password
if [ ! -f "$password_file" ]; then
echo "ERROR: Password file not found at $password_file" | tee -a "$LOG_FILE"
exit 1
fi
# Load and verify password from the file
stored_password=$(cat "$password_file")
if [ "$stored_password" != "$RESTIC_PASSWORD" ]; then
echo "ERROR: Password mismatch. Aborting backup." | tee -a "$LOG_FILE"
exit 1
fi
# Check repository accessibility and integrity
check_backup_repo
if [ $? -ne 0 ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup repository check failed. Aborting backup." | tee -a "$LOG_FILE"
exit 1
fi
# Perform database backup with additional tag
if mysqldump -u "$BACKUP_USER" "$DB_NAME" | restic backup --stdin --tag wordpress_db --tag "$ADDITIONAL_TAG"; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Database backup completed successfully with tags: wordpress_db, $ADDITIONAL_TAG." | tee -a "$LOG_FILE"
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Database backup failed." | tee -a "$LOG_FILE"
exit 1
fi
# Logging end

View File

@ -1,18 +1,22 @@
#!/bin/bash
# Check for required arguments
# Load backup logic functions
source /home/jelastic/mb-backups/backup-logic.sh
# Validate input parameters
if [ "$#" -ne 2 ]; then
echo "Usage: $0 <RESTIC_PASSWORD> <ADDITIONAL_TAG>"
exit 1
fi
# Assign arguments to variables
# Assign command line arguments to variables
RESTIC_PASSWORD="$1"
ADDITIONAL_TAG="$2"
# Configuration
APP_PATH='/var/www/webroot/ROOT'
backupPath='/mnt/backups'
password_file="/etc/restic-password"
LOG_DIR="/home/jelastic/mb-backups/logs/manual/media"
LOG_FILE="${LOG_DIR}/backup_media_$(date +'%Y-%m-%d').log"
includePaths=("$APP_PATH/wp-content/uploads")
@ -25,14 +29,35 @@ export RESTIC_REPOSITORY="$backupPath"
export RESTIC_PASSWORD
# Logging start
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Media Backup" | tee -a "$LOG_FILE"
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Starting Media and Themes Backup" | tee -a "$LOG_FILE"
# Perform backup with additional tag
# Verify that the password file exists and matches the supplied password
if [ ! -f "$password_file" ]; then
echo "ERROR: Password file not found at $password_file" | tee -a "$LOG_FILE"
exit 1
fi
# Load and verify password from the file
stored_password=$(cat "$password_file")
if [ "$stored_password" != "$RESTIC_PASSWORD" ]; then
echo "ERROR: Password mismatch. Aborting backup." | tee -a "$LOG_FILE"
exit 1
fi
# Check repository accessibility and integrity
check_backup_repo
if [ $? -ne 0 ]; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup repository check failed. Aborting backup." | tee -a "$LOG_FILE"
exit 1
fi
# Perform media backup with additional tag
for path in "${includePaths[@]}"; do
if restic backup "$path" --tag media_themes --tag "$ADDITIONAL_TAG"; then
echo "[$(date +'%Y-%m-%d %H:%M:%S')] Backup completed successfully for $path with tags: media_themes, $ADDITIONAL_TAG." | tee -a "$LOG_FILE"
else
echo "[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: Backup failed for $path." | tee -a "$LOG_FILE"
exit 1
fi
done

View File

@ -4,13 +4,18 @@
# Define the script path to check in cron jobs
SCRIPT_PATH="/home/litespeed/mb-backups/backup_all.sh"
LOG_FILE="/home/litespeed/mb-backups/logs/cron_check.log"
# Use crontab -l to list all cron jobs, then grep to check for the specific script path
# Log the start of the script check
echo "[$(date)] Checking for automated backup cron job..." | tee -a "$LOG_FILE"
# Check if the cron job is found in the crontab
CRON_JOB=$(crontab -l | grep -F "$SCRIPT_PATH")
if [ -z "$CRON_JOB" ]; then
echo "Automated Backups is not enabled."
echo "[$(date)] Automated Backups are NOT enabled." | tee -a "$LOG_FILE"
else
echo "$CRON_JOB" | awk '{print "Schedule: " $1, $2, $3, $4, $5}'
# Note: This script stops here, providing the schedule. Computing the next run time in a human-readable format is not straightforward in bash without external tools.
echo "[$(date)] Automated Backups are enabled with the following schedule:" | tee -a "$LOG_FILE"
echo "$CRON_JOB" | awk '{print "Schedule: " $1, $2, $3, $4, $5}' | tee -a "$LOG_FILE"
echo "[$(date)] Note: Computing the next human-readable run time requires external tools." | tee -a "$LOG_FILE"
fi

View File

@ -1,62 +1,196 @@
#!/bin/bash
# Enable strict error handling
set -euo pipefail
trap 'handle_error $? $LINENO' ERR
# Configuration
BACKUP_SCRIPT="/home/litespeed/mb-backups/backup_all.sh"
LOG_DIR="/home/litespeed/mb-backups/logs/auto"
ACTION_LOG_FILE="${LOG_DIR}/schedule_actions.log"
BACKUP_LOG_PREFIX="${LOG_DIR}/backup_"
PASSWORD_FILE="/etc/restic-password"
LOCK_FILE="/tmp/backup_schedule.lock"
MAX_RETRIES=3
RETRY_DELAY=5
# Ensure the log directory exists
mkdir -p "$LOG_DIR"
log_action() {
echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> "$ACTION_LOG_FILE"
# Error handling function
handle_error() {
local exit_code=$1
local line_no=$2
log_action "ERROR: Command failed at line ${line_no} with exit code ${exit_code}"
cleanup_and_exit 1
}
# Function to add or update the cron job with dynamic logging
add_update_cron_job() {
# Verify Restic password is provided
if [ -z "$3" ]; then
echo "Restic password is required."
log_action "Attempted to add/update a schedule without providing a Restic password."
# Cleanup function
cleanup_and_exit() {
local exit_code=$1
[ -f "$LOCK_FILE" ] && rm -f "$LOCK_FILE"
exit "${exit_code}"
}
# Ensure single instance
ensure_single_instance() {
if [ -f "$LOCK_FILE" ]; then
if kill -0 "$(cat "$LOCK_FILE")" 2>/dev/null; then
log_action "ERROR: Another instance is running"
exit 1
fi
# Prepare the cron command to include dynamic date in the backup log filename
CMD="RESTIC_PASSWORD=\"$3\" $BACKUP_SCRIPT > \"${BACKUP_LOG_PREFIX}\$(date +\\%Y-\\%m-\\%d_\\%H-\\%M-\\%S).log\" 2>&1"
# Add or update the cron job
(crontab -l | grep -v "$BACKUP_SCRIPT" 2>/dev/null; echo "$2 $CMD") | crontab -
local update_msg="Backup schedule updated to: $2"
echo "$update_msg"
log_action "$update_msg"
fi
echo $$ > "$LOCK_FILE"
}
# Function to remove the cron job
# Enhanced logging function
log_action() {
local timestamp=$(date +'%Y-%m-%d %H:%M:%S')
local log_msg="[$timestamp] $1"
echo "$log_msg" | tee -a "$ACTION_LOG_FILE"
# Log critical errors to system log
if [[ "$1" == *"ERROR"* ]]; then
logger -t "backup-schedule" "$1"
fi
}
# Validate system requirements
check_system_requirements() {
local required_space=1048576 # 1GB in KB
local available_space=$(df -k "$LOG_DIR" | awk 'NR==2 {print $4}')
if [ ! -x "$BACKUP_SCRIPT" ]; then
log_action "ERROR: Backup script not executable or not found"
return 1
fi
if [ "$available_space" -lt "$required_space" ]; then
log_action "ERROR: Insufficient disk space"
return 1
}
return 0
}
# Validate cron schedule
validate_schedule() {
local schedule="$1"
if ! [[ $schedule =~ ^[0-9,\*/-]+ [0-9,\*/-]+ [0-9,\*/-]+ [0-9,\*/-]+ [0-9,\*/-]+$ ]]; then
log_action "ERROR: Invalid cron schedule format: $schedule"
return 1
fi
return 0
}
# Get Restic password with retry mechanism
get_restic_password() {
local retry_count=0
while [ $retry_count -lt $MAX_RETRIES ]; do
if [ -f "$PASSWORD_FILE" ] && [ -s "$PASSWORD_FILE" ]; then
cat "$PASSWORD_FILE"
return 0
fi
retry_count=$((retry_count + 1))
sleep $RETRY_DELAY
done
log_action "ERROR: Failed to retrieve Restic password after $MAX_RETRIES attempts"
return 1
}
# Enhanced cron job management
add_update_cron_job() {
local action="$1"
local schedule="$2"
local restic_password="$3"
local temp_crontab="/tmp/temp_crontab.$$"
# Validate inputs
if ! validate_schedule "$schedule"; then
return 1
fi
# Prepare cron command with environment variables and error handling
local CMD="RESTIC_PASSWORD=\"$restic_password\" \
LOG_FILE=\"${BACKUP_LOG_PREFIX}\$(date +\\%Y-\\%m-\\%d_\\%H-\\%M-\\%S).log\" \
$BACKUP_SCRIPT > \"\$LOG_FILE\" 2>&1 || echo \"\$(date) Backup failed\" >> \"$ACTION_LOG_FILE\""
# Safely update crontab
crontab -l 2>/dev/null | grep -v "$BACKUP_SCRIPT" > "$temp_crontab" || true
echo "$schedule $CMD" >> "$temp_crontab"
if crontab "$temp_crontab"; then
log_action "Successfully ${action}d backup schedule: $schedule"
rm -f "$temp_crontab"
return 0
else
log_action "ERROR: Failed to ${action} backup schedule"
rm -f "$temp_crontab"
return 1
fi
}
# Enhanced cron job removal
remove_cron_job() {
crontab -l | grep -v "$BACKUP_SCRIPT" | crontab -
local remove_msg="Backup schedule removed."
echo "$remove_msg"
log_action "$remove_msg"
local temp_crontab="/tmp/temp_crontab.$$"
if crontab -l 2>/dev/null | grep -q "$BACKUP_SCRIPT"; then
crontab -l | grep -v "$BACKUP_SCRIPT" > "$temp_crontab"
if crontab "$temp_crontab"; then
log_action "Backup schedule successfully removed"
rm -f "$temp_crontab"
return 0
else
log_action "ERROR: Failed to remove backup schedule"
rm -f "$temp_crontab"
return 1
fi
else
log_action "No backup schedule found to remove"
return 0
fi
}
# Main logic to add, update, or remove the cron job based on user input
# Main execution
main() {
# Create log directory with proper permissions
mkdir -p "$LOG_DIR"
chmod 750 "$LOG_DIR"
# Ensure single instance
ensure_single_instance
# Check system requirements
if ! check_system_requirements; then
cleanup_and_exit 1
fi
case $1 in
add|update)
if [ "$#" -ne 3 ]; then
echo "Usage for add/update: $0 {add|update} 'schedule' 'restic_password'"
echo "Example: $0 add '0 1 * * *' 'secret_password'"
log_action "Incorrect usage for add/update. Correct format not followed."
else
add_update_cron_job "$@"
log_action "Usage: $0 {add|update} 'schedule' 'restic_password'"
log_action "Example: $0 add '0 1 * * *' 'secret_password'"
cleanup_and_exit 1
fi
local restic_password="${3:-$(get_restic_password)}"
if [ -z "$restic_password" ]; then
cleanup_and_exit 1
fi
add_update_cron_job "$1" "$2" "$restic_password"
;;
remove)
remove_cron_job
;;
status)
crontab -l | grep "$BACKUP_SCRIPT" || echo "No backup schedule found"
;;
*)
echo "Invalid action: $1. Use add, update, or remove."
log_action "Invalid action attempted: $1"
exit 1
log_action "Invalid action: $1. Use add, update, remove, or status"
cleanup_and_exit 1
;;
esac
cleanup_and_exit 0
}
# Execute main function with all arguments
main "$@"