seafileBackup/backup_new.sh

481 lines
15 KiB
Bash
Raw Normal View History

2019-05-21 17:27:10 -06:00
#!/bin/sh
#######
### Backup Seafile/Seafile Pro server on bare-metal (NOT DOCKER)
#######
### text formatting presents
norm=$(tput sgr0)
err=$(tput bold)$(tput setaf 1)
warn=$(tput bold)$(tput setaf 3)
ok=$(tput setaf 2)
yellow=$(tput setaf 3)
cyan=$(tput setaf 6)
mag=$(tput setaf 5)
2019-05-21 17:27:10 -06:00
### functions
# bad configuration value passed in details file
badDetails () {
if [ "$1" = "empty" ]; then
exitError 130 "details:${2} cannot be NULL (undefined)"
elif [ "$1" = "dne" ]; then
exitError 131 "details:${2} file or directory does not exist."
fi
}
# bad parameter passed to script
badParam () {
if [ "$1" = "dne" ]; then
2019-05-23 21:42:35 -06:00
printf "${err}\nError: '%s %s'\n" "$2" "$3"
printf "file or directory does not exist.${norm}\n\n"
exit 1
elif [ "$1" = "empty" ]; then
printf "${err}\nError: '%s' cannot have a NULL (empty) value.\n" "$2"
printf "${cyan}Please use '--help' for assistance${norm}\n\n"
exit 1
elif [ "$1" = "svc" ]; then
printf "${err}\nError: '%s %s': Service does not exist!${norm}\n\n" \
"$2" "$3"
exit 1
fi
}
# cleanup
cleanup () {
# cleanup 503 if copied
2019-05-25 12:37:20 -06:00
if [ "$err503Copied" -eq 1 ]; then
if ! rm -f "$webroot/$err503File" 2>>"$logFile"; then
printf "${warn}[%s] -- [WARNING] Could not remove 503 error page." \
"$(stamp)" >> "$logFile"
printf " Web interface will not function until this file is " \
>> "$logFile"
printf "removed --${norm}\n" >> "$logFile"
2019-05-25 13:18:31 -06:00
else
2019-05-25 19:53:00 -06:00
printf "${cyan}[%s] -- [INFO] 503 error page removed --${norm}\n" \
2019-05-25 13:18:31 -06:00
"$(stamp)" >> "$logFile"
fi
fi
2019-05-25 12:37:20 -06:00
if [ "$sqlCopied" -eq 1 ]; then
if ! rm -rf "$sqlDumpDir" 2>>"$logFile"; then
printf "${warn}[%s] -- [WARNING] Could not remove temporary " \
"$(stamp)" >> "$logFile"
printf "SQL dump directory at %s. " "$sqlDumpDir" >> "$logFile"
printf "Remove manually to free up space.${norm}\n" >> "$logFile"
2019-05-25 13:18:31 -06:00
else
2019-05-25 13:29:09 -06:00
printf "${cyan}[%s] -- [INFO] Temporary SQL dump directory " \
2019-05-25 13:18:31 -06:00
"$(stamp)" >> "$logFile"
printf "removed --${norm}\n" >> "$logFile"
fi
fi
2019-05-25 13:11:31 -06:00
if [ "$offlineBackup" -eq 1 ]; then
2019-05-25 13:20:30 -06:00
printf "${cyan}[%s] -- [INFO] Starting seafile services --${norm}\n" \
"$(stamp)" >> "$logFile"
2019-05-25 13:11:31 -06:00
seafSvc start
fi
}
exitError () {
2019-05-23 23:18:35 -06:00
printf "${err}[%s] -- [ERROR] %s: %s --${norm}\n" \
"$(stamp)" "$1" "$2" >> "$logFile"
cleanup
exit "$1"
}
2019-05-22 22:58:07 -06:00
# control seafile services (systemd)
seafSvc () {
if [ "$1" = "start" ]; then
2019-05-23 21:54:19 -06:00
if ! systemctl start "${seafService}" >> "$logFile" 2>&1; then
2019-05-23 23:19:53 -06:00
exitError 100 "Could not start ${seafService}"
else
printf "${cyan}[%s] -- [INFO] Started service: %s --${norm}\n" \
"$(stamp)" "$seafService" >> "$logFile"
2019-05-22 22:58:07 -06:00
fi
if ! systemctl start "${seafHub}" >> "$logFile" 2>&1; then
2019-05-23 23:19:53 -06:00
exitError 101 "Could not start ${seafHub}"
else
printf "${cyan}[%s] -- [INFO] Started service: %s --${norm}\n" \
"$(stamp)" "$seafHub" >> "$logFile"
2019-05-22 22:58:07 -06:00
fi
elif [ "$1" = "stop" ]; then
if ! systemctl stop "${seafHub}" >> "$logFile" 2>&1; then
2019-05-23 23:19:53 -06:00
exitError 103 "Could not stop ${seafHub}"
else
printf "${cyan}[%s] -- [INFO] Stopped service: %s --${norm}\n" \
"$(stamp)" "$seafService" >> "$logFile"
2019-05-22 22:58:07 -06:00
fi
2019-05-23 21:54:19 -06:00
if ! systemctl stop "${seafService}" >> "$logFile" 2>&1; then
2019-05-23 23:19:53 -06:00
exitError 102 "Could not stop ${seafService}"
else
printf "${cyan}[%s] -- [INFO] Stopped service: %s --${norm}\n" \
"$(stamp)" "$seafHub" >> "$logFile"
2019-05-22 22:58:07 -06:00
fi
fi
}
2019-05-21 17:27:10 -06:00
# generate dynamic timestamps
stamp () {
(date +%F" "%T)
}
### end of functions
### default variable values
2019-05-23 20:57:05 -06:00
## script related
2019-05-21 17:27:10 -06:00
# store logfile in the same directory as this script file using the same file
# name as the script but with the extension '.log'
scriptPath="$( CDPATH='' cd -- "$( dirname -- "$0" )" && pwd -P )"
2019-05-21 17:27:10 -06:00
scriptName="$( basename "$0" )"
logFile="$scriptPath/${scriptName%.*}.log"
2019-05-23 20:57:05 -06:00
# borg output verbosity -- normal
borgCreateParams='--stats'
borgPruneParams='--list'
2019-05-23 21:06:49 -06:00
configDetails="$scriptPath/seafbackup.details"
err503Copied=0
sqlCopied=0
2019-05-21 17:27:10 -06:00
2019-05-23 20:57:05 -06:00
# 503 related
2019-05-24 20:41:00 -06:00
use503=0
2019-05-23 20:57:05 -06:00
err503Path="$scriptPath/503_backup.html"
err503File="${err503Path##*/}"
webroot="/usr/share/nginx/html"
# seafile related
2019-05-22 22:58:07 -06:00
offlineBackup=0
seafService="seafile.service"
2019-05-22 22:58:07 -06:00
seafHub="seahub.service"
seafDir="/opt/seafile"
2019-05-23 20:57:05 -06:00
seafData="/var/seafile"
2019-05-23 21:06:49 -06:00
### process startup parameters
while [ $# -gt 0 ]; do
case "$1" in
-h|-\?|--help)
# display help
scriptHelp
exit 0
;;
-l|--log)
# set log file location
if [ -n "$2" ]; then
logFile="${2%/}"
shift
else
badParam empty "$@"
2019-05-23 21:06:49 -06:00
fi
;;
-v|--verbose)
# set verbose logging from borg
borgCreateParams='--list --stats'
borgPruneParams='--list'
;;
-c|--config|--details)
# location of config details file
if [ -n "$2" ]; then
if [ -f "$2" ]; then
configDetails="${2%/}"
shift
else
badParam dne "$@"
fi
2019-05-23 21:06:49 -06:00
else
badParam empty "$@"
2019-05-23 21:06:49 -06:00
fi
;;
2019-05-24 20:41:00 -06:00
-5|--use-503)
# enable copying 503 error page to webroot
use503=1
;;
--503-path)
2019-05-23 21:06:49 -06:00
# FULL path to 503 file
if [ -n "$2" ]; then
2019-05-23 21:43:30 -06:00
if [ -f "$2" ]; then
err503Path="${2%/}"
err503File="${2##*/}"
shift
else
badParam dne "$@"
fi
2019-05-23 21:06:49 -06:00
else
badParam empty "$@"
2019-05-23 21:06:49 -06:00
fi
;;
-w|--webroot)
# path to webroot (copy 503)
if [ -n "$2" ]; then
2019-05-24 21:17:35 -06:00
if [ -d "$2" ]; then
2019-05-23 21:43:30 -06:00
webroot="${2%/}"
shift
else
badParam dne "$@"
fi
2019-05-23 21:06:49 -06:00
else
badParam empty "$@"
2019-05-23 21:06:49 -06:00
fi
;;
-p|--seaf|--seafdir)
# path to seafile program directory
if [ -n "$2" ]; then
2019-05-23 21:43:30 -06:00
if [ -d "$2" ]; then
seafDir="${2%/}"
shift
else
badParam dne "$@"
fi
2019-05-23 21:06:49 -06:00
else
badParam empty "$@"
2019-05-23 21:06:49 -06:00
fi
;;
-d|--data|--datadir|--seafdata)
# path to seafile data directory
if [ -n "$2" ]; then
2019-05-23 21:43:30 -06:00
if [ -d "$2" ]; then
seafData="${2%/}"
shift
else
badParam dne "$@"
fi
2019-05-23 21:06:49 -06:00
else
badParam empty "$@"
2019-05-23 21:06:49 -06:00
fi
;;
2019-05-24 00:31:07 -06:00
--seafile-service)
# name of seafile service
if [ -n "$2" ]; then
if ! systemctl list-unit-files | grep -Eq "^$2 |^$2.service"
then
badParam svc "$@"
else
seafService="${2}"
shift
fi
else
badParam empty "$@"
fi
;;
--seahub-service)
# name of seahub service
if [ -n "$2" ]; then
if ! systemctl list-unit-files | grep -Eq "^$2 |^$2.service"
then
badParam svc "$@"
else
seafHub="${2}"
shift
fi
else
badParam empty "$@"
fi
;;
2019-05-23 21:06:49 -06:00
-o|--offline)
# shutdown seafile during backup
offlineBackup=1
;;
*)
printf "${err}\nUnknown option: %s\n" "$1"
printf "${cyan}Use '--help' for valid options.{$norm}\n\n"
exit 1
;;
esac
shift
done
2019-05-21 17:27:10 -06:00
### check pre-requisites and default values
# does the details file exist?
if [ ! -f "$configDetails" ]; then
2019-05-24 21:18:01 -06:00
badParam dne "(--details default)" "$configDetails"
fi
# is borg installed?
if ! command -v borg > /dev/null; then
printf "\n${err}ERROR: BORG is not installed on this system!${norm}\n\n"
exit 3
fi
2019-05-24 21:34:08 -06:00
# seafile directories
if [ ! -d "$seafDir" ]; then
badParam dne "(--seafdir default)" "$seafDir"
elif [ ! -d "$seafData" ]; then
badParam dne "(--seafdata default)" "$seafData"
fi
# offline backup
2019-05-25 12:37:20 -06:00
if [ "$offlineBackup" -eq 1 ]; then
2019-05-24 21:34:08 -06:00
if ! systemctl list-unit-files | \
grep -Eq "^$seafService |^$seafService.service"
then
badParam svc "(--seafile-service default)" "$seafService"
elif ! systemctl list-unit-files | \
grep -Eq "^$seafHub |^$seafHub.service"
then
badParam svc "(--seahub-service default)" "$seafHub"
fi
fi
# if 503 functionality is enabled, do 503 related files exist?
2019-05-25 12:37:20 -06:00
if [ "$use503" -eq 1 ]; then
if [ ! -f "$err503Path" ]; then
badParam dne "(--503-path default)" "$err503Path"
elif [ ! -d "$webroot" ]; then
badParam dne "(--webroot default)" "$webroot"
fi
fi
2019-05-21 17:27:10 -06:00
### start logging
printf "${mag}[%s] --- Start %s execution ---${norm}\n" \
"$(stamp)" "$scriptName" >> "$logFile"
printf "${cyan}[%s] -- [INFO] Log located at ${yellow}%s${cyan} --${norm}\n" \
"$(stamp)" "$logFile" >> "$logFile"
2019-05-24 23:43:28 -06:00
### 503 functionality
2019-05-25 12:37:20 -06:00
if [ "$use503" -eq 1 ]; then
2019-05-24 23:43:28 -06:00
printf "${cyan}[%s] -- [INFO] Copying 503 error page to " \
"$(stamp)" >> "$logFile"
printf "webroot -- ${norm}\n" >> "$logFile"
if ! \cp --force "${err503Path}" "${webroot}/${err503File}" 2>> "$logFile"
then
2019-05-25 02:17:18 -06:00
printf "${warn}[%s] -- [WARNING] Failed to copy 503 error page. " \
2019-05-24 23:43:28 -06:00
"$(stamp)" >> "$logFile"
2019-05-25 02:32:37 -06:00
printf "Web users will NOT be notified --${norm}\n" >> "$logFile"
2019-05-24 23:43:28 -06:00
else
printf "${ok}[%s] -- [SUCCESS] 503 error page copied --${norm}\n" \
2019-05-24 23:43:28 -06:00
"$(stamp)" >> "$logFile"
# set cleanup flag
err503Copied=1
2019-05-24 23:43:28 -06:00
fi
fi
### stop seahub and seafile service if offline backup requested
if [ "$offlineBackup" -eq 1 ]; then
printf "${cyan}[%s] -- [INFO] Stopping seafile services --${norm}\n" \
"$(stamp)" >> "$logFile"
seafSvc stop
fi
2019-05-22 22:58:07 -06:00
2019-05-25 00:59:23 -06:00
### read details file to get variables needed to dump sql and run borg
# check if config details file was provided as a relative or absolute path
case "${configDetails}" in
/*)
# absolute path, no need to rewrite variable
. "${configDetails}"
;;
*)
# relative path, prepend './' to create absolute path
. "./${configDetails}"
;;
esac
printf "${cyan}[%s] -- [INFO] ${yellow}%s${cyan} imported --${norm}\n" \
"$(stamp)" "$configDetails" >> "$logFile"
2019-05-21 17:27:10 -06:00
2019-05-25 01:55:58 -06:00
### dump sql databases
printf "${cyan}[%s] -- [INFO] Dumping SQL databases --${norm}\n" \
"$(stamp)" >> "$logFile"
# create temporary directory to dump files before borg backup
if ! sqlDumpDir=$( mktemp -d 2>>"$logFile"); then
2019-05-25 01:55:58 -06:00
exitError 111 "Could not create temporary directory to dump SQL files"
fi
# set cleanup flag
sqlCopied=1
2019-05-25 01:55:58 -06:00
printf "${cyan}[%s] -- [INFO] SQL dump files will be temporarily stored in:" \
"$(stamp)" >> "$logFile"
printf "\n${yellow}%s/${cyan} --${norm}\n" "$sqlDumpDir" >> "$logFile"
# create unique names for database dump files
sqlDump_ccnet="backup-$(date +%Y%m%d_%H%M%S)_${ccnetDB_name}.sql"
sqlDump_seafile="backup-$(date +%Y%m%d_%H%M%S)_${seafileDB_name}.sql"
sqlDump_seahub="backup-$(date +%Y%m%d_%H%M%S)_${seahubDB_name}.sql"
## dump databases
# dump CCNET-DB
if ! mysqldump -h"${sqlServer}" -u"${sqlUser}" -p"${sqlPass}" \
--opt ccnet-db > "${sqlDumpDir}/${sqlDump_ccnet}" 2>> "$logFile"; then
exitError 115 "Could not dump ${ccnetDB_name} database"
fi
# dump SEAFILE-DB
if ! mysqldump -h"${sqlServer}" -u"${sqlUser}" -p"${sqlPass}" \
--opt ccnet-db > "${sqlDumpDir}/${sqlDump_seafile}" 2>> "$logFile"; then
exitError 116 "Could not dump ${seafileDB_name} database"
fi
# dump CCNET-DB
if ! mysqldump -h"${sqlServer}" -u"${sqlUser}" -p"${sqlPass}" \
--opt ccnet-db > "${sqlDumpDir}/${sqlDump_seahub}" 2>> "$logFile"; then
exitError 117 "Could not dump ${seahubDB_name} database"
fi
printf "${ok}[%s] -- [SUCCESS] SQL databases dumped successfully --${norm}\n" \
"$(stamp)" >> "$logFile"
2019-05-25 01:55:58 -06:00
2019-05-27 03:08:21 -06:00
### pre-backup tasks completed -- move to borg tasks
printf "${cyan}[%s] -- [INFO] Pre-backup tasks completed --${norm}\n" \
"$(stamp)" >> "$logFile"
### Run borg variable checks
printf "${cyan}[%s] -- [INFO] Verifying supplied borg details --${norm}\n" \
"$(stamp)" >> "$logFile"
## verify borg base directory
if [ -z "${borgBaseDir}" ]; then
badDetails empty 'borgBaseDir'
elif [ ! -d "${borgBaseDir}" ]; then
badDetails dne 'borgBaseDir'
fi
printf "${magenta}details:borgBaseDir ${norm}-- ${ok}[OK]${norm}\n" \
>> "$logfile"
export BORG_BASE_DIR="${borgBaseDir%/}"
## check path to SSH keyfile
if [ -z "${borgSSHKey}" ]; then
badDetails empty 'borgSSHKey'
elif [ ! -d "${borgSSHKey}" ]; then
badDetails dne 'borgSSHKey'
fi
printf "${magenta}details:borgSSHKey ${norm}-- ${ok}[OK]${norm}\n" \
>> "$logfile"
export BORG_RSH="ssh -i ${borgSSHKey}"
## check borg repo connect string
if [ -z "${borgConnectRepo}" ]; then
badDetails empty 'borgConnectRepo'
fi
printf "${magenta}details:borgConnectRepo ${norm}-- ${ok}[OK]${norm}\n" \
>> "$logfile"
export BORG_REPO="${borgConnectRepo}"
## check borg repo password
if [ -n "${borgRepoPassphrase}" ]; then
printf "${magenta}details:borgRepoPassphrase ${norm}-- ${ok}[OK]${norm}\n" \
>> "$logfile"
export BORG_PASSPHRASE="${borgRepoPassphrase}"
else
# if passwd is blank intentionally, this is insecure
printf "${warn} -- [WARNING] Using a borg repo without a password is an " \
>> "$logFile"
printf "insecure configuration --${norm}\n" >> "$logFile"
# if this was an accident, we need to provide a bogus passwd so borg fails
# otherwise it will sit forever just waiting for input
export BORG_PASSPHRASE="DummyPasswordSoBorgFails"
fi
2019-05-25 01:55:58 -06:00
2019-05-21 17:27:10 -06:00
2019-05-23 21:19:28 -06:00
exit 0
### error codes
# 1: parameter error
# 2: not run as root
# 3: borg not installed
2019-05-23 21:19:28 -06:00
# 100: could not start seafile service
# 101: could not start seahub service
# 102: could not stop seafile service
2019-05-25 01:55:58 -06:00
# 103: could not stop seahub service
# 111: could not create tmp dir for SQL dump files
# 115: could not dump CCNET-DB
# 116: could not dump SEAFILE-DB
# 117: could not dump SEAHUB-DB