| Server IP : 172.67.149.12 / Your IP : 104.23.243.226 Web Server : Apache System : Linux vm3310.eosdns.net 4.18.0-553.132.1.el8_10.x86_64 #1 SMP Thu Jun 11 14:27:43 UTC 2026 x86_64 User : carnalmedia ( 1000) PHP Version : 7.4.33 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /mojo/ |
Upload File : |
#!/bin/bash
#
# Script: domain_backup
#
# Description: A script for creating a backup of a single domain and its associated database. It will then make the backup accessible for download. The script is intended to be run as a cron job for clients, who need to download domain backups frequently, but can also be used for one time backup of a domain.
#
# Usage: domain_backup -n [DomainName] -r [DocRoot] -d [Database] -e [E-Mail] -s [DBServer]
#
# Author: Keith Muska
#
# Changelog:
# * 08/31/2016: Keith Muska
# - Initial write
# * 10/20/2016: Keith Muska
# - Fixed bug when backing up sub-directories of domains, such as www.example.com/blog
# - Output now includes download URL
# * 11/08/2016: Keith Muska
# - Security update: Changed .backupdb to .htpasswd to prevent Apache from serving up the password file
# - Feature add: File size now included in e-mail
# - Fixed script header so it will pass the /usr/local/mojo/sbin/headtest
# * 12/16/2016: Keith Muska
# - major change: Switching to getops instead of simple shell options
# - feature add: Allow database server to be set
# - bug fix: drop quotes and trailing slash from mojo.conf SCANPATH variable
# - bug fix: convert All slash characters in domain name to underscore instead of just the first occurrence
# - bug fix: disable ProxyPass in the domain-backup directory
# * 12/29/2016: Keith Muska
# - bug fix: PrxEnable was supposed to be PrxCaptchaEnable...
# * 07/14/2018: Keith Muska
# - bug fix: Fallback to localhost if dbserver fails
# - bug fix: Run mysqldump first to identify issues faster
# * 05/30/2019: Keith Muska
# - bug fix: mysql and mysqldump configured to read /root/.my.cnf when run from cron
# * 13.04.2021: Nikita Shchetinin
# - CentOS 8 compatibility, small adjustments
# help text function.
help_exit () {
echo -e "\nUsage:\n${0} -n [DomainName] -r [DocRoot] -d [Database] -e [E-Mail] -s [DBServer]\n"
echo -e "\nExample: \n ${0} -n example.com -r /home/httpd/html/example.com -d \"example_wp forum_db\" -e \"[email protected] [email protected]\" -s dbserver\n"
echo -e "\nOptions:\n"
echo -e " -n\tName of the Domain"
echo -e " -r\tRoot Directory of the Domain"
echo -e " -d\tDatabase(s).\n\t\tMultiple databases should be space delimited and surrounded in double-quotes."
echo -e " -e\tE-Mail address where notifications should be sent\n\t\tMultiple E-Mail Addresses should be space delimited and surrounded in double-quotes."
echo -e " -s\tDatabase Server. Defaults to \"dbserver\" if none is given."
echo
exit 1
}
# Error handling function. Send e-mail and print to STDERR:
error_exit () {
echo -e "${ERROR}" 1>&2
if [ -n "${EMail}" ]; then
echo -e "Backup of ${Domain} FAILED.\n\n${ERROR}" | mail -s "FAILED Backup of ${Domain}" ${EMail}
fi
cd ${PWD}
help_exit
}
while getopts "n:r:d:e:s:h" OPTION
do
case $OPTION in
n) Domain=${OPTARG//\//_} ;;
r) DocRoot=${OPTARG} ;;
d) Database=${OPTARG} ;;
e) EMail=${OPTARG} ;;
s) DBServer=${OPTARG} ;;
h) help_exit ;;
\?) help_exit ;;
esac
done
# Set Global Variables
MOJOSITEPATH="/home/httpd/html"
SCANPATH="/home/httpd/html"
Hostname=`hostname`
Date=`date +%Y%m%d`
TGZfile="${Domain}.${Date}.${RANDOM}.tgz"
PWD=`pwd`
ERROR=""
# make sure the user supplied a Domain name
if [ -z ${Domain+x} ]; then
ERROR="No Domain Given. Exiting!"
error_exit
fi
#default to "dbserver" if no mysql servername given
if [ -z ${DBServer+x} ] ; then DBServer="dbserver"; fi
# make sure document root exists
if [ ! -d ${DocRoot} ]; then
ERROR="DocRoot: ${DocRoot} Not Found!"
error_exit
fi
# Determine Server's Document Root:
if [[ ${MOJOSITEPATH} != "" ]] && [[ -d ${MOJOSITEPATH} ]]; then
FILESPATH="${MOJOSITEPATH}/domain-backup"
else
FILESPATH="${SCANPATH}/domain-backup"
fi
# Make sure the domain-backup directory exists. If not, attempt to create it.
if [ ! -d ${FILESPATH} ] ; then
mkdir -p ${FILESPATH}
fi
# If the domain-backup directory still doesn't exist, fail.
if [ ! -d ${FILESPATH} ] ; then
ERROR="Directory: '${FILESPATH}' does not exist."
error_exit
fi
# Make sure Authentication is set up. Configure if not:
FILESAUTHCONF="${FILESPATH}/.htaccess"
FILESAUTHDB="${FILESPATH}/.htpasswd"
if [[ ! -f ${FILESAUTHCONF} ]] || [[ $(stat -c %s ${FILESAUTHCONF}) == 0 ]]; then
echo;echo "${FILESAUTHCONF} does not yet exist, creating..."
cat > ${FILESAUTHCONF} <<EOF
<IfModule mod_prxp.c>
PrxEnable Off
</IfModule>
Options -Indexes
AuthUserFile ${FILESAUTHDB}
AuthName "Restricted Area"
AuthType Basic
Require valid-user
EOF
elif [[ $(grep -sw .htpasswd ${FILESAUTHCONF}) == "" ]]; then
echo;echo "${FILESAUTHCONF} is not setup to use .htpasswd for password auth, replacing the auth setup here..."
cat > ${FILESAUTHCONF} <<EOF
<IfModule mod_prxp.c>
PrxCaptchaEnable Off
</IfModule>
Options -Indexes
AuthUserFile ${FILESAUTHDB}
AuthName "Restricted Area"
AuthType Basic
Require valid-user
EOF
fi
if [[ ! -f ${FILESAUTHDB} ]] || [[ $(stat -c %s ${FILESAUTHDB}) == 0 ]]; then
USERNAME=$(</dev/urandom tr -dc A-Za-z0-9 | head -c8)
USERPASS=$(</dev/urandom tr -dc A-Za-z0-9 | head -c16)
echo;echo "${FILESAUTHDB} does not exist or is empty, creating with new user..."
htpasswd -csb ${FILESAUTHDB} ${USERNAME} ${USERPASS}
chmod 644 ${FILESAUTHDB}
echo -e "\n\nSecure Directory configured for Domain Backups.\n==================================================\n\nURL: http://${Hostname}/domain-backup/\nUsername: ${USERNAME}\nPassword: ${USERPASS}\n\n"
fi
# make sure download path is accessible and secure
HTTP_REPL=$(curl -sL -w "%{http_code}\n" ${Hostname}/domain-backup/$ -o /dev/null)
if [ ${HTTP_REPL} -ne 401 ] ; then
ERROR="Incorrect HTTP Response code received.\n\nExpected: 401\nReceived: ${HTTP_REPL}\nURL: ${Hostname}/domain-backup"
error_exit
fi
# Make sure a full copy of the document root won't use more than 20% of the available capacity.
# This may seem like a very conservative figure, but we also need room for the database dumps and TGZ file.
DUDocRoot=`du -ks ${DocRoot} | awk '{print $1}'`
DFFILESPATH=`df -k ${FILESPATH} | grep -v Filesystem | awk '{print $4}'`
if [ $(echo "${DUDocRoot} ${DFFILESPATH}" | awk '{print (($1/$2)>0.20)}') -ne 0 ]; then
ERROR="Backup would consume more than 20% of the remaining drive capacity. Aborting\n"
error_exit
fi
# Test MySQL Connection:
# If first attempt fails, fall back to localhost:
mysql -h${DBServer} -ss -e "SELECT USER(),CURRENT_USER();" > /dev/null
if [ $? -ne 0 ] ; then
echo "Trying \"localhost\" instead."
DBServer="localhost"
mysql -h${DBServer} -ss -e "SELECT USER(),CURRENT_USER();" > /dev/null
if [ $? -ne 0 ] ; then
ERROR="Could not connect to MySQL. Aborting\n"
error_exit
fi
fi
# If we've gotten this far with no errors, it should be safe to proceed:
# Dump the databases
mkdir -p ${FILESPATH}/${Domain}
if [ -n ${Database+x} ]; then
for i in `echo ${Database}` ; do mysqldump -h${DBServer} --skip-extended-insert ${i} > ${FILESPATH}/${Domain}/${i}.${Date}.sql ; done
fi
# Create a backup copy:
rsync -a ${DocRoot}/ ${FILESPATH}/${Domain}/
# And finally, Tar it all up
cd ${FILESPATH} ; tar -czf ${TGZfile} ${Domain}
TGZSize=`du -h ${FILESPATH}/${TGZfile} | cut -f1`
# Remove the working Dir:
if [ -d ${FILESPATH}/${Domain}/ ]; then
rm -rf ${FILESPATH}/${Domain}/
fi
# Send notification.
echo -e "\nBackup of ${Domain} Complete.\n\nYou may download a copy here:\nhttp://${Hostname}/domain-backup/${TGZfile} (Size: ${TGZSize})"
if [ -n ${EMail+x} ]; then
echo -e "Backup of ${Domain} Complete.\n\nYou may download a copy here:\nhttp://${Hostname}/domain-backup/${TGZfile} (Size: ${TGZSize})" | mail -s "Backup of ${Domain}" ${EMail}
fi
cd ${PWD}
exit 0