v-backup-user-without-backups.sh
· 33 KiB · Bash
Brut
#!/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" "<p><span class='u-text-bold'>Archive:</span> <code>$user.$backup_new_date.tar</code></p>"
#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
1 | #!/bin/bash |
2 | # info: backup system user (without backups enabled in plan) with all its objects |
3 | # options: USER |
4 | # |
5 | # example: v-backup-user admin yes |
6 | # |
7 | # This function is used for backing up user with all its domains and databases. |
8 | |
9 | #----------------------------------------------------------# |
10 | # Variables & Functions # |
11 | #----------------------------------------------------------# |
12 | |
13 | # Argument definition |
14 | user=$1 |
15 | notify=${2-no} |
16 | |
17 | # Includes |
18 | # shellcheck source=/etc/hestiacp/hestia.conf |
19 | source /etc/hestiacp/hestia.conf |
20 | # shellcheck source=/usr/local/hestia/func/main.sh |
21 | source $HESTIA/func/main.sh |
22 | # shellcheck source=/usr/local/hestia/func/domain.sh |
23 | source $HESTIA/func/domain.sh |
24 | # shellcheck source=/usr/local/hestia/func/db.sh |
25 | source $HESTIA/func/db.sh |
26 | # shellcheck source=/usr/local/hestia/func/backup.sh |
27 | source $HESTIA/func/backup.sh |
28 | # load config file |
29 | source_conf "$HESTIA/conf/hestia.conf" |
30 | |
31 | |
32 | # Return Disk Usage |
33 | get_user_disk_usage() { |
34 | u_usage=0 |
35 | web_exclusions='' |
36 | mail_exclusions='' |
37 | db_exclusions='' |
38 | user_exclusions='' |
39 | |
40 | # Parsing excludes |
41 | if [ -e "$USER_DATA/backup-excludes.conf" ]; then |
42 | web_exclusions=$(grep 'WEB=' $USER_DATA/backup-excludes.conf \ |
43 | | awk -F "WEB='" '{print $2}' | cut -f 1 -d \') |
44 | mail_exclusions=$(grep 'MAIL=' $USER_DATA/backup-excludes.conf \ |
45 | | awk -F "MAIL='" '{print $2}' | cut -f 1 -d \') |
46 | db_exclusions=$(grep 'DB=' $USER_DATA/backup-excludes.conf \ |
47 | | awk -F "DB='" '{print $2}' | cut -f 1 -d \') |
48 | user_exclusions=$(grep 'USER=' $USER_DATA/backup-excludes.conf \ |
49 | | awk -F "USER='" '{print $2}' | cut -f 1 -d \') |
50 | fi |
51 | |
52 | if [ -f "$USER_DATA/web.conf" ] && [ "$web_exclusions" != '*' ]; then |
53 | usage=0 |
54 | domains=$(grep 'DOMAIN=' $USER_DATA/web.conf \ |
55 | | awk -F "DOMAIN='" '{print $2}' | cut -f 1 -d \') |
56 | |
57 | for domain in $domains; do |
58 | exclusion=$(echo -e "$web_exclusions" | tr ',' '\n' | grep "^$domain\|\*$") |
59 | if [ -z "$exclusion" ]; then |
60 | # Defining home directory |
61 | home_dir="$HOMEDIR/$user/web/$domain/" |
62 | exclusion=$(echo -e "$web_exclusions" | tr ',' '\n' | grep "^$domain\|\*:") |
63 | fargs=() |
64 | |
65 | if [ -n "$exclusion" ]; then |
66 | xdirs=$(echo -e "$exclusion" | tr ':' '\n' | grep -v "$domain\|\*") |
67 | for xpath in $xdirs; do |
68 | fargs+=(--exclude="$xpath") |
69 | done |
70 | fi |
71 | |
72 | # Checking home directory exist |
73 | if [ -e "$home_dir" ]; then |
74 | disk_usage=$(nice -n 19 du -shm "$home_dir" "${fargs[@]}" | cut -f 1) |
75 | u_usage=$((u_usage + disk_usage)) |
76 | fi |
77 | fi |
78 | done |
79 | fi |
80 | |
81 | if [ -f "$USER_DATA/mail.conf" ] && [ "$mail_exclusions" != '*' ]; then |
82 | usage=0 |
83 | domains=$(grep 'DOMAIN=' "$USER_DATA/mail.conf" \ |
84 | | awk -F "DOMAIN='" '{print $2}' | cut -f 1 -d \') |
85 | |
86 | for domain in $domains; do |
87 | check_exl=$(echo "$mail_exclusions" | tr ',' '\n' | grep "^$domain$") |
88 | if [ -f "$USER_DATA/mail/$domain.conf" ] && [ -z "$check_exl" ]; then |
89 | accounts=$(grep 'ACCOUNT=' "$USER_DATA/mail/$domain.conf" \ |
90 | | awk -F "ACCOUNT='" '{print $2}' | cut -f 1 -d \') |
91 | |
92 | for account in $accounts; do |
93 | home_dir=$HOMEDIR/$user/mail/$domain/$account |
94 | exclusion=$(echo "$mail_exclusions" | tr ',' '\n' | grep "$domain:") |
95 | exclusion=$(echo "$exclusion" | tr ':' '\n' | grep -E "^$account|\*") |
96 | |
97 | if [ -z "$exclusion" ] && [ -e "$home_dir" ]; then |
98 | disk_usage=$(nice -n 19 du -shm "$home_dir" | cut -f 1) |
99 | u_usage=$((u_usage + disk_usage)) |
100 | fi |
101 | done |
102 | fi |
103 | done |
104 | fi |
105 | |
106 | if [ -f "$USER_DATA/db.conf" ] && [ "$db_exclusions" != '*' ]; then |
107 | usage=0 |
108 | databases=$(grep 'DB=' "$USER_DATA/db.conf" \ |
109 | | awk -F "DB='" '{print $2}' | cut -f 1 -d \') |
110 | for database in $databases; do |
111 | exclusion=$(echo "$db_exclusions" | tr ',' '\n' | grep "^$database$") |
112 | if [ -z "$exclusion" ]; then |
113 | # Get database values |
114 | get_database_values |
115 | |
116 | # Switching on db type |
117 | case $DB_SYSTEM in |
118 | mysql) get_mysql_disk_usage ;; |
119 | pgsql) get_pgsql_disk_usage ;; |
120 | esac |
121 | u_usage=$((u_usage + usage)) |
122 | fi |
123 | done |
124 | fi |
125 | |
126 | if [ "$user_exclusions" != '*' ]; then |
127 | fargs=() |
128 | for xpath in $(echo "$user_exclusions" | tr ',' '\n'); do |
129 | fargs+=(--exclude="$xpath") |
130 | done |
131 | usage=$(du -shm "$HOMEDIR/$user" --exclude "$HOMEDIR/$user/web" --exclude "$HOMEDIR/$user/mail" --exclude "$HOMEDIR/$user/conf" "${fargs[@]}" | cut -f 1) |
132 | u_usage=$((u_usage + usage)) |
133 | fi |
134 | |
135 | echo ${u_usage} |
136 | } |
137 | |
138 | #----------------------------------------------------------# |
139 | # Verifications # |
140 | #----------------------------------------------------------# |
141 | |
142 | check_args '1' "$#" 'USER [NOTIFY]' |
143 | is_format_valid 'user' |
144 | is_system_enabled "$BACKUP_SYSTEM" 'BACKUP_SYSTEM' |
145 | is_object_valid 'user' 'USER' "$user" |
146 | if [ "$POLICY_BACKUP_SUSPENDED_USERS" != "yes" ]; then |
147 | is_object_unsuspended 'user' 'USER' "$user" |
148 | fi |
149 | #is_backup_enabled |
150 | # If user has backups enabled, exit |
151 | BACKUPS=$(grep "^BACKUPS=" $USER_DATA/user.conf | cut -f2 -d \') |
152 | if [[ "$BACKUPS" -gt '0' ]]; then |
153 | check_result "$E_DISABLED" "backup for user $user is already enabled" |
154 | fi |
155 | |
156 | # Only allow 1 backup for the user |
157 | BACKUPS=1 |
158 | # Perform verification if read-only mode is enabled |
159 | check_hestia_demo_mode |
160 | |
161 | #----------------------------------------------------------# |
162 | # Action # |
163 | #----------------------------------------------------------# |
164 | |
165 | check_backup_conditions |
166 | |
167 | # Set backup directory if undefined |
168 | if [ -z "$BACKUP" ]; then |
169 | BACKUP=/backup |
170 | fi |
171 | |
172 | # Check if backup folder exists and have the correct permission |
173 | if [[ ! -d "$BACKUP" ]]; then |
174 | mkdir -p $BACKUP |
175 | fi |
176 | if [ "$(stat -c %a "$BACKUP")" != 755 ]; then |
177 | chmod 755 $BACKUP |
178 | fi |
179 | |
180 | # Get current time |
181 | start_time=$(date '+%s') |
182 | |
183 | # Set notification email and subject |
184 | #subj="$user → backup failed" |
185 | #email=$(grep CONTACT "$HESTIA/data/users/admin/user.conf" | cut -f 2 -d \') |
186 | |
187 | # Validate available disk space (take usage * 2, due to the backup handling) |
188 | let u_disk=$(($(get_user_disk_usage) * 2)) |
189 | let v_disk=$(($(stat -f --format="%a*%S" $BACKUP)))/1024/1024 |
190 | |
191 | if [ "$u_disk" -gt "$v_disk" ]; then |
192 | let u_disk_original=$(get_user_disk_usage) |
193 | # Always notify on failure |
194 | 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" |
195 | # Deleting task from queue |
196 | sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe |
197 | 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)." |
198 | fi |
199 | |
200 | if [ -z "$BACKUP_TEMP" ]; then |
201 | BACKUP_TEMP=$BACKUP |
202 | fi |
203 | |
204 | # Creating temporary directory |
205 | tmpdir=$(mktemp -p $BACKUP_TEMP -d) |
206 | |
207 | if [ "$?" -ne 0 ]; then |
208 | echo "Can't create tmp dir $tmpdir" | $SENDMAIL -s "$subj" "$email" "yes" |
209 | # Deleting task from queue |
210 | sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe |
211 | check_result "$E_NOTEXIST" "can't create tmp dir" |
212 | fi |
213 | |
214 | # Backup sys configs |
215 | echo "-- SYSTEM --" | tee $BACKUP/$user.log |
216 | mkdir $tmpdir/hestia |
217 | |
218 | echo -e "$(date "+%F %T") $user.conf" | tee -a $BACKUP/$user.log |
219 | cp -r $USER_DATA/user.conf $tmpdir/hestia/ |
220 | cp -r $USER_DATA/ssl $tmpdir/hestia/ |
221 | |
222 | if [ -e "$USER_DATA/stats.log" ]; then |
223 | echo -e "$(date "+%F %T") stats.log" | tee -a $BACKUP/$user.log |
224 | cp -r $USER_DATA/stats.log $tmpdir/hestia/ |
225 | fi |
226 | |
227 | if [ -e "$USER_DATA/history.log" ]; then |
228 | echo -e "$(date "+%F %T") history.log" | tee -a $BACKUP/$user.log |
229 | cp -r $USER_DATA/history.log $tmpdir/hestia/ |
230 | fi |
231 | |
232 | if [ -e "$USER_DATA/backup-excludes.conf" ]; then |
233 | echo -e "$(date "+%F %T") backup-excludes.conf" | tee -a $BACKUP/$user.log |
234 | cp -r $USER_DATA/backup-excludes.conf $tmpdir/hestia/ |
235 | fi |
236 | |
237 | # Backup PAM |
238 | mkdir $tmpdir/pam |
239 | echo -e "$(date "+%F %T") pam" | tee -a $BACKUP/$user.log |
240 | grep "^$user:" /etc/passwd > $tmpdir/pam/passwd |
241 | grep "^$user:" /etc/shadow > $tmpdir/pam/shadow |
242 | grep "^$user:" /etc/group > $tmpdir/pam/group |
243 | echo |
244 | |
245 | # Parsing excludes |
246 | if [ -e "$USER_DATA/backup-excludes.conf" ]; then |
247 | source $USER_DATA/backup-excludes.conf |
248 | fi |
249 | |
250 | # WEB domains |
251 | if [ -n "$WEB_SYSTEM" ] && [ "$WEB" != '*' ]; then |
252 | echo -e "\n-- WEB --" | tee -a $BACKUP/$user.log |
253 | mkdir $tmpdir/web/ |
254 | |
255 | # Parsing domain exclusions |
256 | conf="$USER_DATA/web.conf" |
257 | for domain in $(search_objects 'web' 'SUSPENDED' "*" 'DOMAIN'); do |
258 | exclusion=$(echo -e "$WEB" | tr ',' '\n' | grep "^$domain$\|^\*$") |
259 | if [ -z "$exclusion" ]; then |
260 | web_list="$web_list $domain" |
261 | else |
262 | echo "$(date "+%F %T") excluding $domain" | tee -a $BACKUP/$user.log |
263 | fi |
264 | done |
265 | web_list=$(echo "$web_list" | sed -e "s/ */\ /g" -e "s/^ //") |
266 | |
267 | i=0 |
268 | |
269 | for domain in $web_list; do |
270 | check_backup_conditions |
271 | ((i++)) |
272 | echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log |
273 | mkdir -p $tmpdir/web/$domain/conf |
274 | mkdir -p $tmpdir/web/$domain/hestia |
275 | |
276 | # Get domain variables |
277 | domain_idn=$domain |
278 | format_domain_idn |
279 | get_domain_values 'web' |
280 | |
281 | # Backup web.conf |
282 | cd $tmpdir/web/$domain/ |
283 | conf="$USER_DATA/web.conf" |
284 | grep "DOMAIN='$domain'" $conf > hestia/web.conf |
285 | |
286 | # Backup vhost config |
287 | if [ -e "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.conf" ]; then |
288 | cp $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.conf* conf/ |
289 | elif [ -e "$HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.conf" ]; then |
290 | cp $HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.conf* conf/ |
291 | else |
292 | # legacy format: all domain configs in single file |
293 | tpl_file="$WEBTPL/$WEB_SYSTEM/$WEB_BACKEND/$TPL.tpl" |
294 | conf="$HOMEDIR/$user/conf/web/$WEB_SYSTEM.conf" |
295 | get_web_config_lines "$tpl_file" "$conf" |
296 | sed -n "$top_line,$bottom_line p" $conf > conf/$WEB_SYSTEM.conf |
297 | fi |
298 | |
299 | # Backup ssl vhost |
300 | if [ "$SSL" = 'yes' ]; then |
301 | if [ -e "$HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.ssl.conf" ]; then |
302 | cp $HOMEDIR/$user/conf/web/$domain/$WEB_SYSTEM.ssl.conf* conf/ |
303 | elif [ -e "$HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.ssl.conf" ]; then |
304 | cp $HOMEDIR/$user/conf/web/$domain.$WEB_SYSTEM.ssl.conf* conf/ |
305 | else |
306 | # legacy format: all domain configs in single file |
307 | tpl_file="$WEBTPL/$WEB_SYSTEM/$WEB_BACKEND/$TPL.stpl" |
308 | conf="$HOMEDIR/$user/conf/web/s$WEB_SYSTEM.conf" |
309 | get_web_config_lines "$tpl_file" "$conf" |
310 | sed -n "$top_line,$bottom_line p" $conf > conf/s$WEB_SYSTEM.conf |
311 | fi |
312 | fi |
313 | |
314 | # Backup proxy config |
315 | if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then |
316 | if [ -e "$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.conf" ]; then |
317 | cp $HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.conf* conf/ |
318 | elif [ -e "$HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.conf" ]; then |
319 | cp $HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.conf* conf/ |
320 | else |
321 | # legacy format: all domain configs in single file |
322 | tpl_file="$WEBTPL/$PROXY_SYSTEM/$PROXY.tpl" |
323 | conf="$HOMEDIR/$user/conf/web/$PROXY_SYSTEM.conf" |
324 | get_web_config_lines "$tpl_file" "$conf" |
325 | sed -n "$top_line,$bottom_line p" $conf > conf/$PROXY_SYSTEM.conf |
326 | fi |
327 | fi |
328 | |
329 | # Backup ssl proxy config |
330 | if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ] && [ "$SSL" = 'yes' ]; then |
331 | if [ -e "$HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.ssl.conf" ]; then |
332 | cp $HOMEDIR/$user/conf/web/$domain/$PROXY_SYSTEM.ssl.conf* conf/ |
333 | elif [ -e "$HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.ssl.conf" ]; then |
334 | cp $HOMEDIR/$user/conf/web/$domain.$PROXY_SYSTEM.ssl.conf* conf/ |
335 | else |
336 | # legacy format: all domain configs in single file |
337 | tpl_file="$WEBTPL/$PROXY_SYSTEM/$PROXY.stpl" |
338 | conf="$HOMEDIR/$user/conf/web/s$PROXY_SYSTEM.conf" |
339 | get_web_config_lines "$tpl_file" "$conf" |
340 | sed -n "$top_line,$bottom_line p" $conf > conf/s$PROXY_SYSTEM.conf |
341 | fi |
342 | fi |
343 | |
344 | domain_conf=$(grep "DOMAIN='$domain'" $conf) |
345 | parse_object_kv_list_non_eval domain_conf |
346 | |
347 | mkdir -p template/$WEB_SYSTEM/ |
348 | mkdir template/php-fpm/ |
349 | |
350 | if [ $WEB_BACKEND == 'php-fpm' ]; then |
351 | cp $HESTIA/data/templates/web/$WEB_SYSTEM/php-fpm/$TPL.tpl template/$WEB_SYSTEM/ |
352 | cp $HESTIA/data/templates/web/$WEB_SYSTEM/php-fpm/$TPL.stpl template/$WEB_SYSTEM/ |
353 | cp $HESTIA/data/templates/web/php-fpm/$BACKEND.tpl template/php-fpm/ |
354 | else |
355 | cp $HESTIA/data/templates/web/$WEB_SYSTEM/$TPL.tpl template/$WEB_SYSTEM/ |
356 | cp $HESTIA/data/templates/web/$WEB_SYSTEM/$TPL.stpl template/$WEB_SYSTEM/ |
357 | fi |
358 | if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then |
359 | mkdir template/$PROXY_SYSTEM |
360 | cp $HESTIA/data/templates/web/$PROXY_SYSTEM/$PROXY.tpl template/$PROXY_SYSTEM/ |
361 | cp $HESTIA/data/templates/web/$PROXY_SYSTEM/$PROXY.stpl template/$PROXY_SYSTEM/ |
362 | fi |
363 | |
364 | # Backup ssl certificates |
365 | if [ "$SSL" = 'yes' ]; then |
366 | cp $HOMEDIR/$user/conf/web/$domain/ssl/$domain.* conf/ |
367 | cp $USER_DATA/ssl/$domain.* hestia/ |
368 | fi |
369 | |
370 | # Changin dir to documentroot |
371 | cd $HOMEDIR/$user/web/$domain |
372 | |
373 | # Define exclude arguments |
374 | exclusion=$(echo -e "$WEB" | tr ',' '\n' | grep "^$domain\|\*:") |
375 | set -f |
376 | fargs=() |
377 | fargs+=(--exclude='./logs/*') |
378 | if [ -n "$exclusion" ]; then |
379 | |
380 | if [[ "$exclusion" =~ '*' ]]; then |
381 | exclusion="${exclusion/\*/$domain}" |
382 | fi |
383 | |
384 | xdirs="$(echo -e "$exclusion" | tr ':' '\n' | grep -v $domain)" |
385 | for xpath in $xdirs; do |
386 | if [ -d "$xpath" ]; then |
387 | fargs+=(--exclude=$xpath/*) |
388 | echo "$(date "+%F %T") excluding directory $xpath" |
389 | msg="$msg\n$(date "+%F %T") excluding directory $xpath" |
390 | else |
391 | echo "$(date "+%F %T") excluding file $xpath" |
392 | msg="$msg\n$(date "+%F %T") excluding file $xpath" |
393 | fargs+=(--exclude=$xpath) |
394 | fi |
395 | done |
396 | fi |
397 | set +f |
398 | |
399 | # Backup files |
400 | if [ "$BACKUP_MODE" = 'zstd' ]; then |
401 | tar "${fargs[@]}" -cpf- * | pzstd -"$BACKUP_GZIP" - > $tmpdir/web/$domain/domain_data.tar.zst |
402 | else |
403 | tar "${fargs[@]}" -cpf- * | gzip -"$BACKUP_GZIP" - > $tmpdir/web/$domain/domain_data.tar.gz |
404 | fi |
405 | done |
406 | |
407 | # Print total |
408 | if [ "$i" -eq 1 ]; then |
409 | echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log |
410 | else |
411 | echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log |
412 | fi |
413 | fi |
414 | |
415 | # DNS domains |
416 | if [ -n "$DNS_SYSTEM" ] && [ "$DNS" != '*' ]; then |
417 | echo -e "\n-- DNS --" | tee -a $BACKUP/$user.log |
418 | mkdir $tmpdir/dns/ |
419 | |
420 | # Parsing domain exclusions |
421 | for domain in $(search_objects 'dns' 'SUSPENDED' "*" 'DOMAIN'); do |
422 | exclusion=$(echo "$DNS" | tr ',' '\n' | grep "^$domain$") |
423 | if [ -z "$exclusion" ]; then |
424 | dns_list="$dns_list $domain" |
425 | else |
426 | echo "$(date "+%F %T") excluding $domain" |
427 | msg="$msg\n$(date "+%F %T") excluding $domain" |
428 | fi |
429 | done |
430 | dns_list=$(echo "$dns_list" | sed -e "s/ */\ /g" -e "s/^ //") |
431 | |
432 | i=0 |
433 | for domain in $dns_list; do |
434 | ((i++)) |
435 | echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log |
436 | domain_idn="$domain" |
437 | # Building directory tree |
438 | mkdir -p $tmpdir/dns/$domain/conf |
439 | mkdir -p $tmpdir/dns/$domain/conf/keys |
440 | mkdir -p $tmpdir/dns/$domain/hestia |
441 | |
442 | # Backup dns.conf |
443 | cd $tmpdir/dns/$domain/ |
444 | conf="$USER_DATA/dns.conf" |
445 | grep "DOMAIN='$domain'" $conf > hestia/dns.conf |
446 | |
447 | # Backup dns recods |
448 | cp $USER_DATA/dns/$domain.conf hestia/$domain.conf |
449 | if [ "$DNS_SYSTEM" != 'remote' ]; then |
450 | cp $HOMEDIR/$user/conf/dns/$domain.db conf/$domain.db |
451 | fi |
452 | # Backup DNSSEC public and private key if enabled |
453 | dnssec=$(grep "DOMAIN='$domain'" $USER_DATA/dns.conf | grep "DNSSEC='yes'") |
454 | if [ -n "$dnssec" ]; then |
455 | format_domain_idn |
456 | cp $USER_DATA/keys/K$domain_idn*.* $tmpdir/dns/$domain/conf/keys |
457 | fi |
458 | done |
459 | |
460 | # Print total |
461 | if [ "$i" -eq 1 ]; then |
462 | echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log |
463 | else |
464 | echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log |
465 | fi |
466 | fi |
467 | |
468 | # Mail domains |
469 | if [ -n "$MAIL_SYSTEM" ] && [ "$MAIL" != '*' ]; then |
470 | echo -e "\n-- MAIL --" | tee -a $BACKUP/$user.log |
471 | mkdir $tmpdir/mail/ |
472 | |
473 | # Parsing domain exclusions |
474 | conf="$USER_DATA/mail.conf" |
475 | for domain in $(search_objects 'mail' 'SUSPENDED' "*" 'DOMAIN'); do |
476 | check_exl=$(echo "$MAIL" | tr ',' '\n' | grep "^$domain$") |
477 | if [ -z "$check_exl" ]; then |
478 | mail_list="$mail_list $domain" |
479 | else |
480 | echo "$(date "+%F %T") excluding $domain" | tee -a $BACKUP/$user.log |
481 | fi |
482 | done |
483 | mail_list=$(echo "$mail_list" | sed -e "s/ */\ /g" -e "s/^ //") |
484 | |
485 | i=0 |
486 | for domain in $mail_list; do |
487 | check_backup_conditions |
488 | ((i++)) |
489 | echo -e "$(date "+%F %T") $domain" | tee -a $BACKUP/$user.log |
490 | mkdir -p $tmpdir/mail/$domain/conf |
491 | mkdir -p $tmpdir/mail/$domain/hestia |
492 | domain_idn=$domain |
493 | format_domain_idn |
494 | |
495 | # Backup exim config |
496 | if [[ "$MAIL_SYSTEM" =~ exim ]]; then |
497 | cd $tmpdir/mail/$domain/ |
498 | cp -r $HOMEDIR/$user/conf/mail/$domain/* conf/ |
499 | fi |
500 | |
501 | # Backup mail.conf |
502 | conf="$USER_DATA/mail.conf" |
503 | grep "DOMAIN='$domain'" $conf > hestia/mail.conf |
504 | cp $USER_DATA/mail/$domain.* hestia/ |
505 | if [ -n "$(ls $USER_DATA/mail/ | grep *@$domain)" ]; then |
506 | cp $USER_DATA/mail/*@$domain.* hestia/ |
507 | fi |
508 | |
509 | # Backup emails |
510 | cd $HOMEDIR/$user/mail/$domain_idn |
511 | accounts=() |
512 | for account in *; do |
513 | exclusion=$(echo "$MAIL" | tr ',' '\n' | grep "$domain:") |
514 | exclusion=$(echo "$exclusion" | tr ':' '\n' | grep -E "^$account|\*") |
515 | |
516 | # Checking exclusions |
517 | if [ -z "$exclusion" ] && [[ "$MAIL_SYSTEM" =~ exim ]]; then |
518 | accounts+=($account) |
519 | else |
520 | echo "$(date "+%F %T") excluding mail account $account" \ |
521 | | tee -a $BACKUP/$user.log |
522 | fi |
523 | done |
524 | # Compress archive |
525 | if [ ${#accounts[@]} -gt 0 ]; then |
526 | if [ "$BACKUP_MODE" = 'zstd' ]; then |
527 | tar -cpf- "${accounts[@]}" | pzstd -"$BACKUP_GZIP" - > $tmpdir/mail/$domain/accounts.tar.zst |
528 | else |
529 | tar -cpf- "${accounts[@]}" | gzip -"$BACKUP_GZIP" - > $tmpdir/mail/$domain/accounts.tar.gz |
530 | fi |
531 | fi |
532 | done |
533 | |
534 | # Print total |
535 | if [ "$i" -eq 1 ]; then |
536 | echo -e "$(date "+%F %T") *** $i domain ***" | tee -a $BACKUP/$user.log |
537 | else |
538 | echo -e "$(date "+%F %T") *** $i domains ***" | tee -a $BACKUP/$user.log |
539 | fi |
540 | fi |
541 | |
542 | # Databases |
543 | if [ -n "$DB_SYSTEM" ] && [ "$DB" != '*' ]; then |
544 | echo -e "\n-- DB --" | tee -a $BACKUP/$user.log |
545 | mkdir $tmpdir/db/ |
546 | |
547 | # Parsing database exclusions |
548 | for database in $(search_objects 'db' 'SUSPENDED' "*" 'DB'); do |
549 | exclusion=$(echo "$DB" | tr ',' '\n' | grep "^$database$") |
550 | if [ -z "$exclusion" ]; then |
551 | db_list="$db_list $database" |
552 | else |
553 | echo "$(date "+%F %T") excluding $database" \ |
554 | | tee -a $BACKUP/$user.log |
555 | fi |
556 | done |
557 | |
558 | i=0 |
559 | conf="$USER_DATA/db.conf" |
560 | db_list=$(echo "$db_list" | sed -e "s/ */\ /g" -e "s/^ //") |
561 | for database in $db_list; do |
562 | check_backup_conditions |
563 | ((i++)) |
564 | get_database_values |
565 | |
566 | echo -e "$(date "+%F %T") $database ($TYPE)" | tee -a $BACKUP/$user.log |
567 | mkdir -p $tmpdir/db/$database/conf |
568 | mkdir -p $tmpdir/db/$database/hestia |
569 | |
570 | cd $tmpdir/db/$database/ |
571 | grep "DB='$database'" $conf > hestia/db.conf |
572 | |
573 | dump="$tmpdir/db/$database/$database.$TYPE.sql" |
574 | if [ "$BACKUP_MODE" = 'zstd' ]; then |
575 | dumpgz="$tmpdir/db/$database/$database.$TYPE.sql.zst" |
576 | else |
577 | dumpgz="$tmpdir/db/$database/$database.$TYPE.sql.gz" |
578 | fi |
579 | |
580 | grants="$tmpdir/db/$database/conf/$database.$TYPE.$DBUSER" |
581 | if [ ! -f "$dumpgz" ]; then |
582 | |
583 | WAIT_LOOP_ENTERED=0 |
584 | while true; do |
585 | if pgrep -x "mysqldump" > /dev/null; then |
586 | WAIT_LOOP_ENTERED=1 |
587 | echo "Wait other mysqldump to finish" |
588 | sleep 1 |
589 | else |
590 | if [ "$WAIT_LOOP_ENTERED" -eq 1 ]; then |
591 | echo "We can use mysqldump now" |
592 | fi |
593 | break |
594 | fi |
595 | done |
596 | |
597 | case $TYPE in |
598 | mysql) dump_mysql_database ;; |
599 | pgsql) dump_pgsql_database ;; |
600 | esac |
601 | |
602 | # Compress dump |
603 | if [ "$BACKUP_MODE" = 'zstd' ]; then |
604 | pzstd -$BACKUP_GZIP $dump |
605 | rm $dump |
606 | else |
607 | gzip -$BACKUP_GZIP $dump |
608 | fi |
609 | fi |
610 | done |
611 | |
612 | # Print total |
613 | if [ "$i" -eq 1 ]; then |
614 | echo -e "$(date "+%F %T") *** $i database ***" \ |
615 | | tee -a $BACKUP/$user.log |
616 | else |
617 | echo -e "$(date "+%F %T") *** $i databases ***" \ |
618 | | tee -a $BACKUP/$user.log |
619 | fi |
620 | fi |
621 | |
622 | # Cron jobs |
623 | if [ -n "$CRON_SYSTEM" ] && [ "$CRON" != '*' ]; then |
624 | echo -e "\n-- CRON --" | tee -a $BACKUP/$user.log |
625 | mkdir $tmpdir/cron/ |
626 | |
627 | # Backup cron.conf |
628 | cp $USER_DATA/cron.conf $tmpdir/cron/ |
629 | cron_record=$(wc -l $USER_DATA/cron.conf | cut -f 1 -d ' ') |
630 | |
631 | if [ -e "/var/spool/cron/$user" ]; then |
632 | cron_list="$cron_record" |
633 | cp /var/spool/cron/$user $tmpdir/cron/ |
634 | fi |
635 | |
636 | # Print total |
637 | if [ "$cron_record" -eq 1 ]; then |
638 | echo -e "$(date "+%F %T") *** $cron_record job ***" \ |
639 | | tee -a $BACKUP/$user.log |
640 | else |
641 | echo -e "$(date "+%F %T") *** $cron_record jobs ***" \ |
642 | | tee -a $BACKUP/$user.log |
643 | fi |
644 | fi |
645 | |
646 | # User Directories |
647 | if [ "$USER" != '*' ]; then |
648 | echo -e "\n-- User Dir --" | tee -a $BACKUP/$user.log |
649 | mkdir $tmpdir/user_dir |
650 | cd $HOMEDIR/$user |
651 | |
652 | # Parsing directory exclusions |
653 | USER='' |
654 | if [ -e "$USER_DATA/backup-excludes.conf" ]; then |
655 | source $USER_DATA/backup-excludes.conf |
656 | fi |
657 | fargs=() |
658 | for xpath in $(echo "$USER" | tr ',' '\n'); do |
659 | if [ -d "$xpath" ]; then |
660 | fargs+=(--exclude="$xpath"/*) |
661 | echo "$(date "+%F %T") excluding directory $xpath" \ |
662 | | tee -a $BACKUP/$user.log |
663 | else |
664 | echo "$(date "+%F %T") excluding file $xpath" \ |
665 | | tee -a $BACKUP/$user.log |
666 | fargs+=(--exclude="$xpath") |
667 | fi |
668 | done |
669 | |
670 | IFS=$'\n' |
671 | set -f |
672 | i=0 |
673 | |
674 | for udir in $(ls -a | egrep -v "^conf$|^web$|^dns$|^tmp$|^mail$|^\.\.$|^\.$"); do |
675 | exclusion=$(echo "$USER" | tr ',' '\n' | grep "^$udir$") |
676 | if [ -z "$exclusion" ]; then |
677 | ((i++)) |
678 | udir_list="$udir_list $udir" |
679 | echo -e "$(date "+%F %T") adding $udir" | tee -a $BACKUP/$user.log |
680 | |
681 | check_backup_conditions |
682 | |
683 | # Backup files and dirs |
684 | if [ "$BACKUP_MODE" = 'zstd' ]; then |
685 | tar --anchored -cpf- "${fargs[@]}" $udir | pzstd -"$BACKUP_GZIP" - > $tmpdir/user_dir/$udir.tar.zst |
686 | else |
687 | tar --anchored -cpf- "${fargs[@]}" $udir | gzip -"$BACKUP_GZIP" - > $tmpdir/user_dir/$udir.tar.gz |
688 | fi |
689 | |
690 | fi |
691 | done |
692 | set +f |
693 | udir_list=$(echo "$udir_list" | sed -e "s/ */\ /g" -e "s/^ //") |
694 | |
695 | # Print total |
696 | if [ "$i" -eq 1 ]; then |
697 | echo -e "$(date "+%F %T") *** $i user directory ***" \ |
698 | | tee -a $BACKUP/$user.log |
699 | else |
700 | echo -e "$(date "+%F %T") *** $i directories ***" \ |
701 | | tee -a $BACKUP/$user.log |
702 | fi |
703 | fi |
704 | |
705 | if [ "$BACKUP_MODE" = 'zstd' ]; then |
706 | touch $tmpdir/.zstd |
707 | fi |
708 | |
709 | # Get backup size |
710 | size="$(du -shm $tmpdir | cut -f 1)" |
711 | |
712 | # Get current time |
713 | end_time=$(date '+%s') |
714 | time_n_date=$(date +'%T %F') |
715 | time=$(echo "$time_n_date" | cut -f 1 -d \ ) |
716 | date=$(echo "$time_n_date" | cut -f 2 -d \ ) |
717 | backup_new_date=$(date +"%Y-%m-%d_%H-%M-%S") |
718 | |
719 | echo -e "\n-- SUMMARY --" | tee -a $BACKUP/$user.log |
720 | |
721 | errorcode="0" |
722 | # Switching on backup system types |
723 | for backup_type in $(echo -e "${BACKUP_SYSTEM//,/\\n}"); do |
724 | case $backup_type in |
725 | local) local_backup ;; |
726 | ftp) ftp_backup ;; |
727 | sftp) sftp_backup ;; |
728 | b2) b2_backup ;; |
729 | rclone) rclone_backup ;; |
730 | esac |
731 | done |
732 | |
733 | # Removing tmpdir |
734 | rm -rf $tmpdir |
735 | if [[ "$errorcode" != "0" ]]; then |
736 | if [[ "$BACKUP_SYSTEM" =~ "local" ]]; then |
737 | echo -e "$(date "+%F %T") *** Local backup was successfully executed. Remote backup failed ***" \ |
738 | | tee -a $BACKUP/$user.log |
739 | BACKUP_SYSTEM="local" |
740 | else |
741 | echo -e "$(date "+%F %T") *** Remote backup failed ***" \ |
742 | | tee -a $BACKUP/$user.log |
743 | |
744 | exit $error_code |
745 | fi |
746 | fi |
747 | # Calculation run time |
748 | run_time=$((end_time - start_time)) |
749 | run_time=$((run_time / 60)) |
750 | current_time=$(date "+%T") |
751 | if [ "$run_time" -lt 1 ]; then |
752 | run_time=1 |
753 | fi |
754 | min=minutes |
755 | if [ "$run_time" -eq 1 ]; then |
756 | min=minute |
757 | fi |
758 | |
759 | echo "$(date "+%F %T") Size: $size MB" | tee -a $BACKUP/$user.log |
760 | echo "$(date "+%F %T") Runtime: $run_time $min" | tee -a $BACKUP/$user.log |
761 | |
762 | #----------------------------------------------------------# |
763 | # Hestia # |
764 | #----------------------------------------------------------# |
765 | |
766 | # Removing duplicate |
767 | #touch $USER_DATA/backup.conf |
768 | #sed -i "/$user.$backup_new_date.tar/d" $USER_DATA/backup.conf |
769 | |
770 | # Registering new backup |
771 | #backup_str="BACKUP='$user.$backup_new_date.tar'" |
772 | #backup_str="$backup_str TYPE='$BACKUP_SYSTEM' SIZE='$size'" |
773 | #backup_str="$backup_str WEB='${web_list// /,}'" |
774 | #backup_str="$backup_str DNS='${dns_list// /,}'" |
775 | #backup_str="$backup_str MAIL='${mail_list// /,}'" |
776 | #backup_str="$backup_str DB='${db_list// /,}'" |
777 | #backup_str="$backup_str CRON='$cron_list'" |
778 | #backup_str="$backup_str UDIR='${udir_list// /,}'" |
779 | #backup_str="$backup_str RUNTIME='$run_time' TIME='$time' DATE='$date'" |
780 | #echo "$backup_str" >> $USER_DATA/backup.conf |
781 | |
782 | # Removing old backups |
783 | #tail -n $BACKUPS $USER_DATA/backup.conf > $USER_DATA/backup.conf_ |
784 | #mv -f $USER_DATA/backup.conf_ $USER_DATA/backup.conf |
785 | #chmod 660 $USER_DATA/backup.conf |
786 | #chmod 660 $BACKUP/$user.log |
787 | |
788 | # Deleting task from queue |
789 | sed -i "/v-backup-user $user /d" $HESTIA/data/queue/backup.pipe |
790 | |
791 | #U_BACKUPS=$(grep BACKUP $USER_DATA/backup.conf | wc -l) |
792 | #update_user_value "$user" '$U_BACKUPS' "$U_BACKUPS" |
793 | cd $BACKUP |
794 | |
795 | # Send notification |
796 | #if [ -e "$BACKUP/$user.log" ] && [ "$notify" = "yes" ]; then |
797 | # subj="$user → backup has been completed" |
798 | # email=$(get_user_value '$CONTACT') |
799 | # cat $BACKUP/$user.log | $SENDMAIL -s "$subj" "$email" "$notify" |
800 | # $BIN/v-add-user-notification "$user" "Backup created successfully" "<p><span class='u-text-bold'>Archive:</span> <code>$user.$backup_new_date.tar</code></p>" |
801 | #fi |
802 | |
803 | # Logging |
804 | #$BIN/v-log-action "$user" "Info" "Backup" "Backup created (Archive: $backup_new_date.tar)." |
805 | $BIN/v-log-action "system" "Info" "Backup" "Backup created (User: $user, Archive: $backup_new_date.tar)." |
806 | log_event "$OK" "$ARGUMENTS" |
807 | |
808 | exit |