#!/bin/bash # info: backup system user (without backups enabled in plan) with all its objects # options: USER # # example: v-backup-user admin yes # # This function is used for backing up user with all its domains and databases. #----------------------------------------------------------# # Variables & Functions # #----------------------------------------------------------# # Argument definition user=$1 notify=${2-no} # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh source $HESTIA/func/main.sh # shellcheck source=/usr/local/hestia/func/domain.sh source $HESTIA/func/domain.sh # shellcheck source=/usr/local/hestia/func/db.sh source $HESTIA/func/db.sh # shellcheck source=/usr/local/hestia/func/backup.sh source $HESTIA/func/backup.sh # load config file source_conf "$HESTIA/conf/hestia.conf" # Return Disk Usage get_user_disk_usage() { u_usage=0 web_exclusions='' mail_exclusions='' db_exclusions='' user_exclusions='' # Parsing excludes if [ -e "$USER_DATA/backup-excludes.conf" ]; then web_exclusions=$(grep 'WEB=' $USER_DATA/backup-excludes.conf \ | awk -F "WEB='" '{print $2}' | cut -f 1 -d \') mail_exclusions=$(grep 'MAIL=' $USER_DATA/backup-excludes.conf \ | awk -F "MAIL='" '{print $2}' | cut -f 1 -d \') db_exclusions=$(grep 'DB=' $USER_DATA/backup-excludes.conf \ | awk -F "DB='" '{print $2}' | cut -f 1 -d \') user_exclusions=$(grep 'USER=' $USER_DATA/backup-excludes.conf \ | awk -F "USER='" '{print $2}' | cut -f 1 -d \') fi if [ -f "$USER_DATA/web.conf" ] && [ "$web_exclusions" != '*' ]; then usage=0 domains=$(grep 'DOMAIN=' $USER_DATA/web.conf \ | awk -F "DOMAIN='" '{print $2}' | cut -f 1 -d \') for domain in $domains; do exclusion=$(echo -e "$web_exclusions" | tr ',' '\n' | grep "^$domain\|\*$") if [ -z "$exclusion" ]; then # Defining home directory home_dir="$HOMEDIR/$user/web/$domain/" exclusion=$(echo -e "$web_exclusions" | tr ',' '\n' | grep "^$domain\|\*:") fargs=() if [ -n "$exclusion" ]; then xdirs=$(echo -e "$exclusion" | tr ':' '\n' | grep -v "$domain\|\*") for xpath in $xdirs; do fargs+=(--exclude="$xpath") done fi # Checking home directory exist if [ -e "$home_dir" ]; then disk_usage=$(nice -n 19 du -shm "$home_dir" "${fargs[@]}" | cut -f 1) u_usage=$((u_usage + disk_usage)) fi fi done fi if [ -f "$USER_DATA/mail.conf" ] && [ "$mail_exclusions" != '*' ]; then usage=0 domains=$(grep 'DOMAIN=' "$USER_DATA/mail.conf" \ | awk -F "DOMAIN='" '{print $2}' | cut -f 1 -d \') for domain in $domains; do check_exl=$(echo "$mail_exclusions" | tr ',' '\n' | grep "^$domain$") if [ -f "$USER_DATA/mail/$domain.conf" ] && [ -z "$check_exl" ]; then accounts=$(grep 'ACCOUNT=' "$USER_DATA/mail/$domain.conf" \ | awk -F "ACCOUNT='" '{print $2}' | cut -f 1 -d \') for account in $accounts; do home_dir=$HOMEDIR/$user/mail/$domain/$account exclusion=$(echo "$mail_exclusions" | tr ',' '\n' | grep "$domain:") exclusion=$(echo "$exclusion" | tr ':' '\n' | grep -E "^$account|\*") if [ -z "$exclusion" ] && [ -e "$home_dir" ]; then disk_usage=$(nice -n 19 du -shm "$home_dir" | cut -f 1) u_usage=$((u_usage + disk_usage)) fi done fi done fi if [ -f "$USER_DATA/db.conf" ] && [ "$db_exclusions" != '*' ]; then usage=0 databases=$(grep 'DB=' "$USER_DATA/db.conf" \ | awk -F "DB='" '{print $2}' | cut -f 1 -d \') for database in $databases; do exclusion=$(echo "$db_exclusions" | tr ',' '\n' | grep "^$database$") if [ -z "$exclusion" ]; then # Get database values get_database_values # Switching on db type case $DB_SYSTEM in mysql) get_mysql_disk_usage ;; pgsql) get_pgsql_disk_usage ;; esac u_usage=$((u_usage + usage)) fi done fi if [ "$user_exclusions" != '*' ]; then fargs=() for xpath in $(echo "$user_exclusions" | tr ',' '\n'); do fargs+=(--exclude="$xpath") done usage=$(du -shm "$HOMEDIR/$user" --exclude "$HOMEDIR/$user/web" --exclude "$HOMEDIR/$user/mail" --exclude "$HOMEDIR/$user/conf" "${fargs[@]}" | cut -f 1) u_usage=$((u_usage + usage)) fi echo ${u_usage} } #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# check_args '1' "$#" 'USER [NOTIFY]' is_format_valid 'user' is_system_enabled "$BACKUP_SYSTEM" 'BACKUP_SYSTEM' is_object_valid 'user' 'USER' "$user" if [ "$POLICY_BACKUP_SUSPENDED_USERS" != "yes" ]; then is_object_unsuspended 'user' 'USER' "$user" fi #is_backup_enabled # If user has backups enabled, exit BACKUPS=$(grep "^BACKUPS=" $USER_DATA/user.conf | cut -f2 -d \') if [[ "$BACKUPS" -gt '0' ]]; then check_result "$E_DISABLED" "backup for user $user is already enabled" fi # Only allow 1 backup for the user BACKUPS=1 # Perform verification if read-only mode is enabled check_hestia_demo_mode #----------------------------------------------------------# # Action # #----------------------------------------------------------# check_backup_conditions # Set backup directory if undefined if [ -z "$BACKUP" ]; then BACKUP=/backup fi # Check if backup folder exists and have the correct permission if [[ ! -d "$BACKUP" ]]; then mkdir -p $BACKUP fi if [ "$(stat -c %a "$BACKUP")" != 755 ]; then chmod 755 $BACKUP fi # Get current time start_time=$(date '+%s') # Set notification email and subject #subj="$user → backup failed" #email=$(grep CONTACT "$HESTIA/data/users/admin/user.conf" | cut -f 2 -d \') # Validate available disk space (take usage * 2, due to the backup handling) let u_disk=$(($(get_user_disk_usage) * 2)) let v_disk=$(($(stat -f --format="%a*%S" $BACKUP)))/1024/1024 if [ "$u_disk" -gt "$v_disk" ]; then let u_disk_original=$(get_user_disk_usage) # Always notify on failure echo "Not enough disk space available ($v_disk mb) to perform the backup of $user. ( $u_disk_original mb * 2 = $u_disk mb). https://hestiacp.com/docs/server-administration/backup-restore.html" | $SENDMAIL -s "$subj" "$email" "yes" # Deleting task from queue sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe check_result "$E_LIMIT" "not enough disk space available ($v_disk mb) to perform the backup of $user. ( $u_disk_original mb * 2 = $u_disk mb)." fi if [ -z "$BACKUP_TEMP" ]; then BACKUP_TEMP=$BACKUP fi # Creating temporary directory tmpdir=$(mktemp -p $BACKUP_TEMP -d) if [ "$?" -ne 0 ]; then echo "Can't create tmp dir $tmpdir" | $SENDMAIL -s "$subj" "$email" "yes" # Deleting task from queue sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe check_result "$E_NOTEXIST" "can't create tmp dir" fi # Backup sys configs echo "-- SYSTEM --" | tee $BACKUP/$user.log mkdir $tmpdir/hestia echo -e "$(date "+%F %T") $user.conf" | tee -a $BACKUP/$user.log cp -r $USER_DATA/user.conf $tmpdir/hestia/ cp -r $USER_DATA/ssl $tmpdir/hestia/ if [ -e "$USER_DATA/stats.log" ]; then echo -e "$(date "+%F %T") stats.log" | tee -a $BACKUP/$user.log cp -r $USER_DATA/stats.log $tmpdir/hestia/ fi if [ -e "$USER_DATA/history.log" ]; then echo -e "$(date "+%F %T") history.log" | tee -a $BACKUP/$user.log cp -r $USER_DATA/history.log $tmpdir/hestia/ fi if [ -e "$USER_DATA/backup-excludes.conf" ]; then echo -e "$(date "+%F %T") backup-excludes.conf" | tee -a $BACKUP/$user.log cp -r $USER_DATA/backup-excludes.conf $tmpdir/hestia/ fi # Backup PAM mkdir $tmpdir/pam echo -e "$(date "+%F %T") pam" | tee -a $BACKUP/$user.log grep "^$user:" /etc/passwd > $tmpdir/pam/passwd grep "^$user:" /etc/shadow > $tmpdir/pam/shadow grep "^$user:" /etc/group > $tmpdir/pam/group echo # Parsing excludes if [ -e "$USER_DATA/backup-excludes.conf" ]; then source $USER_DATA/backup-excludes.conf fi # WEB domains if [ -n "$WEB_SYSTEM" ] && [ "$WEB" != '*' ]; then echo -e "\n-- WEB --" | tee -a $BACKUP/$user.log mkdir $tmpdir/web/ # Parsing domain exclusions conf="$USER_DATA/web.conf" for domain in $(search_objects 'web' 'SUSPENDED' "*" 'DOMAIN'); do exclusion=$(echo -e "$WEB" | tr ',' '\n' | grep "^$domain$\|^\*$") if [ -z "$exclusion" ]; then web_list="$web_list $domain" else echo "$(date "+%F %T") excluding $domain" | tee -a $BACKUP/$user.log fi done web_list=$(echo "$web_list" | sed -e "s/ */\ /g" -e "s/^ //") i=0 for domain in $web_list; do check_backup_conditions ((i++)) echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log mkdir -p $tmpdir/web/$domain/conf mkdir -p $tmpdir/web/$domain/hestia # Get domain variables domain_idn=$domain format_domain_idn get_domain_values 'web' # Backup web.conf cd $tmpdir/web/$domain/ conf="$USER_DATA/web.conf" grep "DOMAIN='$domain'" $conf > hestia/web.conf # Backup vhost config if [ -e "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.conf* conf/ elif [ -e "$HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.conf* conf/ else # legacy format: all domain configs in single file tpl_file="$WEBTPL/$WEB_SYSTEM/$WEB_BACKEND/$TPL.tpl" conf="$HOMEDIR/$user/conf/web/$WEB_SYSTEM.conf" get_web_config_lines "$tpl_file" "$conf" sed -n "$top_line,$bottom_line p" $conf > conf/$WEB_SYSTEM.conf fi # Backup ssl vhost if [ "$SSL" = 'yes' ]; then if [ -e "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.ssl.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.ssl.conf* conf/ elif [ -e "$HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.ssl.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.ssl.conf* conf/ else # legacy format: all domain configs in single file tpl_file="$WEBTPL/$WEB_SYSTEM/$WEB_BACKEND/$TPL.stpl" conf="$HOMEDIR/$user/conf/web/s$WEB_SYSTEM.conf" get_web_config_lines "$tpl_file" "$conf" sed -n "$top_line,$bottom_line p" $conf > conf/s$WEB_SYSTEM.conf fi fi # Backup proxy config if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then if [ -e "$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.conf* conf/ elif [ -e "$HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.conf* conf/ else # legacy format: all domain configs in single file tpl_file="$WEBTPL/$PROXY_SYSTEM/$PROXY.tpl" conf="$HOMEDIR/$user/conf/web/$PROXY_SYSTEM.conf" get_web_config_lines "$tpl_file" "$conf" sed -n "$top_line,$bottom_line p" $conf > conf/$PROXY_SYSTEM.conf fi fi # Backup ssl proxy config if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ] && [ "$SSL" = 'yes' ]; then if [ -e "$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.ssl.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.ssl.conf* conf/ elif [ -e "$HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.ssl.conf" ]; then cp $HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.ssl.conf* conf/ else # legacy format: all domain configs in single file tpl_file="$WEBTPL/$PROXY_SYSTEM/$PROXY.stpl" conf="$HOMEDIR/$user/conf/web/s$PROXY_SYSTEM.conf" get_web_config_lines "$tpl_file" "$conf" sed -n "$top_line,$bottom_line p" $conf > conf/s$PROXY_SYSTEM.conf fi fi domain_conf=$(grep "DOMAIN='$domain'" $conf) parse_object_kv_list_non_eval domain_conf mkdir -p template/$WEB_SYSTEM/ mkdir template/php-fpm/ if [ $WEB_BACKEND == 'php-fpm' ]; then cp $HESTIA/data/templates/web/$WEB_SYSTEM/php-fpm/$TPL.tpl template/$WEB_SYSTEM/ cp $HESTIA/data/templates/web/$WEB_SYSTEM/php-fpm/$TPL.stpl template/$WEB_SYSTEM/ cp $HESTIA/data/templates/web/php-fpm/$BACKEND.tpl template/php-fpm/ else cp $HESTIA/data/templates/web/$WEB_SYSTEM/$TPL.tpl template/$WEB_SYSTEM/ cp $HESTIA/data/templates/web/$WEB_SYSTEM/$TPL.stpl template/$WEB_SYSTEM/ fi if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then mkdir template/$PROXY_SYSTEM cp $HESTIA/data/templates/web/$PROXY_SYSTEM/$PROXY.tpl template/$PROXY_SYSTEM/ cp $HESTIA/data/templates/web/$PROXY_SYSTEM/$PROXY.stpl template/$PROXY_SYSTEM/ fi # Backup ssl certificates if [ "$SSL" = 'yes' ]; then cp $HOMEDIR/$user/conf/web/$domain/ssl/$domain.* conf/ cp $USER_DATA/ssl/$domain.* hestia/ fi # Changin dir to documentroot cd $HOMEDIR/$user/web/$domain # Define exclude arguments exclusion=$(echo -e "$WEB" | tr ',' '\n' | grep "^$domain\|\*:") set -f fargs=() fargs+=(--exclude='./logs/*') if [ -n "$exclusion" ]; then if [[ "$exclusion" =~ '*' ]]; then exclusion="${exclusion/\*/$domain}" fi xdirs="$(echo -e "$exclusion" | tr ':' '\n' | grep -v $domain)" for xpath in $xdirs; do if [ -d "$xpath" ]; then fargs+=(--exclude=$xpath/*) echo "$(date "+%F %T") excluding directory $xpath" msg="$msg\n$(date "+%F %T") excluding directory $xpath" else echo "$(date "+%F %T") excluding file $xpath" msg="$msg\n$(date "+%F %T") excluding file $xpath" fargs+=(--exclude=$xpath) fi done fi set +f # Backup files if [ "$BACKUP_MODE" = 'zstd' ]; then tar "${fargs[@]}" -cpf- * | pzstd -"$BACKUP_GZIP" - > $tmpdir/web/$domain/domain_data.tar.zst else tar "${fargs[@]}" -cpf- * | gzip -"$BACKUP_GZIP" - > $tmpdir/web/$domain/domain_data.tar.gz fi done # Print total if [ "$i" -eq 1 ]; then echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log else echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log fi fi # DNS domains if [ -n "$DNS_SYSTEM" ] && [ "$DNS" != '*' ]; then echo -e "\n-- DNS --" | tee -a $BACKUP/$user.log mkdir $tmpdir/dns/ # Parsing domain exclusions for domain in $(search_objects 'dns' 'SUSPENDED' "*" 'DOMAIN'); do exclusion=$(echo "$DNS" | tr ',' '\n' | grep "^$domain$") if [ -z "$exclusion" ]; then dns_list="$dns_list $domain" else echo "$(date "+%F %T") excluding $domain" msg="$msg\n$(date "+%F %T") excluding $domain" fi done dns_list=$(echo "$dns_list" | sed -e "s/ */\ /g" -e "s/^ //") i=0 for domain in $dns_list; do ((i++)) echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log domain_idn="$domain" # Building directory tree mkdir -p $tmpdir/dns/$domain/conf mkdir -p $tmpdir/dns/$domain/conf/keys mkdir -p $tmpdir/dns/$domain/hestia # Backup dns.conf cd $tmpdir/dns/$domain/ conf="$USER_DATA/dns.conf" grep "DOMAIN='$domain'" $conf > hestia/dns.conf # Backup dns recods cp $USER_DATA/dns/$domain.conf hestia/$domain.conf if [ "$DNS_SYSTEM" != 'remote' ]; then cp $HOMEDIR/$user/conf/dns/$domain.db conf/$domain.db fi # Backup DNSSEC public and private key if enabled dnssec=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf | grep "DNSSEC='yes'") if [ -n "$dnssec" ]; then format_domain_idn cp $USER_DATA/keys/K$domain_idn*.* $tmpdir/dns/$domain/conf/keys fi done # Print total if [ "$i" -eq 1 ]; then echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log else echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log fi fi # Mail domains if [ -n "$MAIL_SYSTEM" ] && [ "$MAIL" != '*' ]; then echo -e "\n-- MAIL --" | tee -a $BACKUP/$user.log mkdir $tmpdir/mail/ # Parsing domain exclusions conf="$USER_DATA/mail.conf" for domain in $(search_objects 'mail' 'SUSPENDED' "*" 'DOMAIN'); do check_exl=$(echo "$MAIL" | tr ',' '\n' | grep "^$domain$") if [ -z "$check_exl" ]; then mail_list="$mail_list $domain" else echo "$(date "+%F %T") excluding $domain" | tee -a $BACKUP/$user.log fi done mail_list=$(echo "$mail_list" | sed -e "s/ */\ /g" -e "s/^ //") i=0 for domain in $mail_list; do check_backup_conditions ((i++)) echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log mkdir -p $tmpdir/mail/$domain/conf mkdir -p $tmpdir/mail/$domain/hestia domain_idn=$domain format_domain_idn # Backup exim config if [[ "$MAIL_SYSTEM" =~ exim ]]; then cd $tmpdir/mail/$domain/ cp -r $HOMEDIR/$user/conf/mail/$domain/* conf/ fi # Backup mail.conf conf="$USER_DATA/mail.conf" grep "DOMAIN='$domain'" $conf > hestia/mail.conf cp $USER_DATA/mail/$domain.* hestia/ if [ -n "$(ls $USER_DATA/mail/ | grep *@$domain)" ]; then cp $USER_DATA/mail/*@$domain.* hestia/ fi # Backup emails cd $HOMEDIR/$user/mail/$domain_idn accounts=() for account in *; do exclusion=$(echo "$MAIL" | tr ',' '\n' | grep "$domain:") exclusion=$(echo "$exclusion" | tr ':' '\n' | grep -E "^$account|\*") # Checking exclusions if [ -z "$exclusion" ] && [[ "$MAIL_SYSTEM" =~ exim ]]; then accounts+=($account) else echo "$(date "+%F %T") excluding mail account $account" \ | tee -a $BACKUP/$user.log fi done # Compress archive if [ ${#accounts[@]} -gt 0 ]; then if [ "$BACKUP_MODE" = 'zstd' ]; then tar -cpf- "${accounts[@]}" | pzstd -"$BACKUP_GZIP" - > $tmpdir/mail/$domain/accounts.tar.zst else tar -cpf- "${accounts[@]}" | gzip -"$BACKUP_GZIP" - > $tmpdir/mail/$domain/accounts.tar.gz fi fi done # Print total if [ "$i" -eq 1 ]; then echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log else echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log fi fi # Databases if [ -n "$DB_SYSTEM" ] && [ "$DB" != '*' ]; then echo -e "\n-- DB --" | tee -a $BACKUP/$user.log mkdir $tmpdir/db/ # Parsing database exclusions for database in $(search_objects 'db' 'SUSPENDED' "*" 'DB'); do exclusion=$(echo "$DB" | tr ',' '\n' | grep "^$database$") if [ -z "$exclusion" ]; then db_list="$db_list $database" else echo "$(date "+%F %T") excluding $database" \ | tee -a $BACKUP/$user.log fi done i=0 conf="$USER_DATA/db.conf" db_list=$(echo "$db_list" | sed -e "s/ */\ /g" -e "s/^ //") for database in $db_list; do check_backup_conditions ((i++)) get_database_values echo -e "$(date "+%F %T") $database ($TYPE)" | tee -a $BACKUP/$user.log mkdir -p $tmpdir/db/$database/conf mkdir -p $tmpdir/db/$database/hestia cd $tmpdir/db/$database/ grep "DB='$database'" $conf > hestia/db.conf dump="$tmpdir/db/$database/$database.$TYPE.sql" if [ "$BACKUP_MODE" = 'zstd' ]; then dumpgz="$tmpdir/db/$database/$database.$TYPE.sql.zst" else dumpgz="$tmpdir/db/$database/$database.$TYPE.sql.gz" fi grants="$tmpdir/db/$database/conf/$database.$TYPE.$DBUSER" if [ ! -f "$dumpgz" ]; then WAIT_LOOP_ENTERED=0 while true; do if pgrep -x "mysqldump" > /dev/null; then WAIT_LOOP_ENTERED=1 echo "Wait other mysqldump to finish" sleep 1 else if [ "$WAIT_LOOP_ENTERED" -eq 1 ]; then echo "We can use mysqldump now" fi break fi done case $TYPE in mysql) dump_mysql_database ;; pgsql) dump_pgsql_database ;; esac # Compress dump if [ "$BACKUP_MODE" = 'zstd' ]; then pzstd -$BACKUP_GZIP $dump rm $dump else gzip -$BACKUP_GZIP $dump fi fi done # Print total if [ "$i" -eq 1 ]; then echo -e "$(date "+%F %T") *** $i database ***" \ | tee -a $BACKUP/$user.log else echo -e "$(date "+%F %T") *** $i databases ***" \ | tee -a $BACKUP/$user.log fi fi # Cron jobs if [ -n "$CRON_SYSTEM" ] && [ "$CRON" != '*' ]; then echo -e "\n-- CRON --" | tee -a $BACKUP/$user.log mkdir $tmpdir/cron/ # Backup cron.conf cp $USER_DATA/cron.conf $tmpdir/cron/ cron_record=$(wc -l $USER_DATA/cron.conf | cut -f 1 -d ' ') if [ -e "/var/spool/cron/$user" ]; then cron_list="$cron_record" cp /var/spool/cron/$user $tmpdir/cron/ fi # Print total if [ "$cron_record" -eq 1 ]; then echo -e "$(date "+%F %T") *** $cron_record job ***" \ | tee -a $BACKUP/$user.log else echo -e "$(date "+%F %T") *** $cron_record jobs ***" \ | tee -a $BACKUP/$user.log fi fi # User Directories if [ "$USER" != '*' ]; then echo -e "\n-- User Dir --" | tee -a $BACKUP/$user.log mkdir $tmpdir/user_dir cd $HOMEDIR/$user # Parsing directory exclusions USER='' if [ -e "$USER_DATA/backup-excludes.conf" ]; then source $USER_DATA/backup-excludes.conf fi fargs=() for xpath in $(echo "$USER" | tr ',' '\n'); do if [ -d "$xpath" ]; then fargs+=(--exclude="$xpath"/*) echo "$(date "+%F %T") excluding directory $xpath" \ | tee -a $BACKUP/$user.log else echo "$(date "+%F %T") excluding file $xpath" \ | tee -a $BACKUP/$user.log fargs+=(--exclude="$xpath") fi done IFS=$'\n' set -f i=0 for udir in $(ls -a | egrep -v "^conf$|^web$|^dns$|^tmp$|^mail$|^\.\.$|^\.$"); do exclusion=$(echo "$USER" | tr ',' '\n' | grep "^$udir$") if [ -z "$exclusion" ]; then ((i++)) udir_list="$udir_list $udir" echo -e "$(date "+%F %T") adding $udir" | tee -a $BACKUP/$user.log check_backup_conditions # Backup files and dirs if [ "$BACKUP_MODE" = 'zstd' ]; then tar --anchored -cpf- "${fargs[@]}" $udir | pzstd -"$BACKUP_GZIP" - > $tmpdir/user_dir/$udir.tar.zst else tar --anchored -cpf- "${fargs[@]}" $udir | gzip -"$BACKUP_GZIP" - > $tmpdir/user_dir/$udir.tar.gz fi fi done set +f udir_list=$(echo "$udir_list" | sed -e "s/ */\ /g" -e "s/^ //") # Print total if [ "$i" -eq 1 ]; then echo -e "$(date "+%F %T") *** $i user directory ***" \ | tee -a $BACKUP/$user.log else echo -e "$(date "+%F %T") *** $i directories ***" \ | tee -a $BACKUP/$user.log fi fi if [ "$BACKUP_MODE" = 'zstd' ]; then touch $tmpdir/.zstd fi # Get backup size size="$(du -shm $tmpdir | cut -f 1)" # Get current time end_time=$(date '+%s') time_n_date=$(date +'%T %F') time=$(echo "$time_n_date" | cut -f 1 -d \ ) date=$(echo "$time_n_date" | cut -f 2 -d \ ) backup_new_date=$(date +"%Y-%m-%d_%H-%M-%S") echo -e "\n-- SUMMARY --" | tee -a $BACKUP/$user.log errorcode="0" # Switching on backup system types for backup_type in $(echo -e "${BACKUP_SYSTEM//,/\\n}"); do case $backup_type in local) local_backup ;; ftp) ftp_backup ;; sftp) sftp_backup ;; b2) b2_backup ;; rclone) rclone_backup ;; esac done # Removing tmpdir rm -rf $tmpdir if [[ "$errorcode" != "0" ]]; then if [[ "$BACKUP_SYSTEM" =~ "local" ]]; then echo -e "$(date "+%F %T") *** Local backup was successfully executed. Remote backup failed ***" \ | tee -a $BACKUP/$user.log BACKUP_SYSTEM="local" else echo -e "$(date "+%F %T") *** Remote backup failed ***" \ | tee -a $BACKUP/$user.log exit $error_code fi fi # Calculation run time run_time=$((end_time - start_time)) run_time=$((run_time / 60)) current_time=$(date "+%T") if [ "$run_time" -lt 1 ]; then run_time=1 fi min=minutes if [ "$run_time" -eq 1 ]; then min=minute fi echo "$(date "+%F %T") Size: $size MB" | tee -a $BACKUP/$user.log echo "$(date "+%F %T") Runtime: $run_time $min" | tee -a $BACKUP/$user.log #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Removing duplicate #touch $USER_DATA/backup.conf #sed -i "/$user.$backup_new_date.tar/d" $USER_DATA/backup.conf # Registering new backup #backup_str="BACKUP='$user.$backup_new_date.tar'" #backup_str="$backup_str TYPE='$BACKUP_SYSTEM' SIZE='$size'" #backup_str="$backup_str WEB='${web_list// /,}'" #backup_str="$backup_str DNS='${dns_list// /,}'" #backup_str="$backup_str MAIL='${mail_list// /,}'" #backup_str="$backup_str DB='${db_list// /,}'" #backup_str="$backup_str CRON='$cron_list'" #backup_str="$backup_str UDIR='${udir_list// /,}'" #backup_str="$backup_str RUNTIME='$run_time' TIME='$time' DATE='$date'" #echo "$backup_str" >> $USER_DATA/backup.conf # Removing old backups #tail -n $BACKUPS $USER_DATA/backup.conf > $USER_DATA/backup.conf_ #mv -f $USER_DATA/backup.conf_ $USER_DATA/backup.conf #chmod 660 $USER_DATA/backup.conf #chmod 660 $BACKUP/$user.log # Deleting task from queue sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe #U_BACKUPS=$(grep BACKUP $USER_DATA/backup.conf | wc -l) #update_user_value "$user" '$U_BACKUPS' "$U_BACKUPS" cd $BACKUP # Send notification #if [ -e "$BACKUP/$user.log" ] && [ "$notify" = "yes" ]; then # subj="$user → backup has been completed" # email=$(get_user_value '$CONTACT') # cat $BACKUP/$user.log | $SENDMAIL -s "$subj" "$email" "$notify" # $BIN/v-add-user-notification "$user" "Backup created successfully" "

Archive: $user.$backup_new_date.tar

" #fi # Logging #$BIN/v-log-action "$user" "Info" "Backup" "Backup created (Archive: $backup_new_date.tar)." $BIN/v-log-action "system" "Info" "Backup" "Backup created (User: $user, Archive: $backup_new_date.tar)." log_event "$OK" "$ARGUMENTS" exit