Initial import of borg-restore
This commit is contained in:
Executable
+129
@@ -0,0 +1,129 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
CONFIG_FILE="${PLUGIN_DIR}/plugin-settings.conf"
|
||||
DATA_DIR="${PLUGIN_DIR}/data"
|
||||
JOB_DIR="${DATA_DIR}/jobs"
|
||||
LOG_DIR="${DATA_DIR}/logs"
|
||||
PID_DIR="${DATA_DIR}/pids"
|
||||
CANCEL_DIR="${DATA_DIR}/cancel"
|
||||
TMP_DIR="${DATA_DIR}/tmp"
|
||||
CSRF_DIR="${DATA_DIR}/csrf"
|
||||
|
||||
warn() {
|
||||
printf '[fix_permissions] %s\n' "$*" >&2
|
||||
}
|
||||
|
||||
strip_quotes() {
|
||||
local v="$1"
|
||||
v="${v#"${v%%[![:space:]]*}"}"
|
||||
v="${v%"${v##*[![:space:]]}"}"
|
||||
if [[ "$v" == \"*\" && "$v" == *\" ]]; then
|
||||
v="${v:1:${#v}-2}"
|
||||
elif [[ "$v" == \'*\' && "$v" == *\' ]]; then
|
||||
v="${v:1:${#v}-2}"
|
||||
fi
|
||||
printf '%s' "$v"
|
||||
}
|
||||
|
||||
get_cfg() {
|
||||
local key="$1"
|
||||
local value=""
|
||||
if [ -r "$CONFIG_FILE" ]; then
|
||||
while IFS= read -r line; do
|
||||
line="${line%%#*}"
|
||||
line="$(strip_quotes "$line")"
|
||||
if [[ "$line" == "$key="* ]]; then
|
||||
value="${line#*=}"
|
||||
fi
|
||||
done < "$CONFIG_FILE"
|
||||
fi
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
owner_name="$(stat -c %U "$PLUGIN_DIR" 2>/dev/null || true)"
|
||||
if [ "$owner_name" != "root" ] && [ "$owner_name" != "diradmin" ]; then
|
||||
owner_name="root"
|
||||
fi
|
||||
owner_group="$owner_name"
|
||||
|
||||
fix_dir() {
|
||||
local path="$1"
|
||||
if [ -z "$path" ]; then
|
||||
return
|
||||
fi
|
||||
mkdir -p "$path"
|
||||
chown "$owner_name":"$owner_group" "$path" || true
|
||||
chmod 700 "$path" || true
|
||||
}
|
||||
|
||||
fix_file() {
|
||||
local path="$1"
|
||||
local label="$2"
|
||||
if [ -z "$path" ]; then
|
||||
return
|
||||
fi
|
||||
if [ ! -e "$path" ]; then
|
||||
warn "${label} missing: ${path}"
|
||||
return
|
||||
fi
|
||||
if [ -L "$path" ] || [ ! -f "$path" ]; then
|
||||
warn "${label} not a regular file: ${path}"
|
||||
return
|
||||
fi
|
||||
chown "$owner_name":"$owner_group" "$path" || true
|
||||
chmod 600 "$path" || true
|
||||
}
|
||||
|
||||
BB_PATH="$(get_cfg "BB_PATH")"
|
||||
BORG_VARIABLES_FILE="$(get_cfg "BORG_VARIABLES_FILE")"
|
||||
|
||||
if [ -z "$BB_PATH" ]; then
|
||||
BB_PATH="/opt/bb"
|
||||
fi
|
||||
|
||||
bbvars_path=""
|
||||
if [ -n "$BORG_VARIABLES_FILE" ]; then
|
||||
bbvars_path="${BORG_VARIABLES_FILE}"
|
||||
if [[ "$bbvars_path" == *DA_USER* ]]; then
|
||||
if [ -n "${DA_USER:-}" ]; then
|
||||
bbvars_path="${bbvars_path//DA_USER/${DA_USER}}"
|
||||
elif [ -f "/opt/bb/bbvars.sh" ]; then
|
||||
bbvars_path="/opt/bb/bbvars.sh"
|
||||
else
|
||||
bbvars_path=""
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [[ "$BB_PATH" == *.sh ]]; then
|
||||
bbvars_path="$BB_PATH"
|
||||
else
|
||||
bbvars_path="${BB_PATH%/}/bbvars.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
fix_dir "$DATA_DIR"
|
||||
fix_dir "$JOB_DIR/pending"
|
||||
fix_dir "$JOB_DIR/processing"
|
||||
fix_dir "$JOB_DIR/done"
|
||||
fix_dir "$LOG_DIR"
|
||||
fix_dir "$PID_DIR"
|
||||
fix_dir "$CANCEL_DIR"
|
||||
fix_dir "$TMP_DIR"
|
||||
fix_dir "$CSRF_DIR"
|
||||
|
||||
fix_file "$CONFIG_FILE" "plugin-settings.conf"
|
||||
if [ -n "$bbvars_path" ]; then
|
||||
fix_file "$bbvars_path" "bbvars.sh"
|
||||
else
|
||||
warn "bbvars.sh path not resolved; skipped"
|
||||
fi
|
||||
|
||||
for f in "$JOB_DIR"/pending/*.env "$JOB_DIR"/processing/*.env "$JOB_DIR"/done/*.{ok,fail,cancel,env.ok,env.fail,env.cancel}; do
|
||||
if [ -f "$f" ]; then
|
||||
fix_file "$f" "job file"
|
||||
fi
|
||||
done
|
||||
|
||||
exit 0
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
# Ensure expected permissions and ownership so DirectAdmin can update plugin.conf.
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
chown -R diradmin:diradmin "$PLUGIN_DIR" || true
|
||||
else
|
||||
echo "borg-restore: warning: not running as root, cannot chown to diradmin" >&2
|
||||
fi
|
||||
|
||||
chmod 755 "$PLUGIN_DIR" "$PLUGIN_DIR/hooks" "$PLUGIN_DIR/user" "$PLUGIN_DIR/scripts" "$PLUGIN_DIR/images" || true
|
||||
chmod 755 "$PLUGIN_DIR/user/index.html" "$PLUGIN_DIR/scripts/install.sh" "$PLUGIN_DIR/scripts/uninstall.sh" "$PLUGIN_DIR/scripts/restore.sh" "$PLUGIN_DIR/scripts/worker.sh" || true
|
||||
chmod 644 "$PLUGIN_DIR/plugin.conf" "$PLUGIN_DIR/hooks/user_txt.html" "$PLUGIN_DIR/plugin-settings.conf" "$PLUGIN_DIR/user/enhanced.css" "$PLUGIN_DIR/user/evolution.css" "$PLUGIN_DIR/images/user_icon.svg" || true
|
||||
|
||||
DATA_DIR="$PLUGIN_DIR/data"
|
||||
mkdir -p "$DATA_DIR/jobs/pending" "$DATA_DIR/jobs/processing" "$DATA_DIR/jobs/done" "$DATA_DIR/logs" "$DATA_DIR/pids" "$DATA_DIR/cancel"
|
||||
chmod 700 "$DATA_DIR" "$DATA_DIR/jobs" "$DATA_DIR/jobs/pending" "$DATA_DIR/jobs/processing" "$DATA_DIR/jobs/done" "$DATA_DIR/logs" "$DATA_DIR/pids" "$DATA_DIR/cancel" || true
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
chown -R diradmin:diradmin "$DATA_DIR" || true
|
||||
fi
|
||||
|
||||
CRON_FILE="/etc/cron.d/borg-restore"
|
||||
if [ "$(id -u)" -eq 0 ]; then
|
||||
echo "* * * * * root /usr/local/directadmin/plugins/borg-restore/scripts/worker.sh >/dev/null 2>&1" > "$CRON_FILE"
|
||||
chmod 644 "$CRON_FILE" || true
|
||||
else
|
||||
echo "borg-restore: warning: not running as root, cannot install cron job" >&2
|
||||
fi
|
||||
|
||||
echo "borg-restore: install complete"
|
||||
Executable
+495
@@ -0,0 +1,495 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
ERROR_MSG=""
|
||||
SUCCESS_MSG=""
|
||||
RESTORE_OK=0
|
||||
DA_USER=""
|
||||
|
||||
JOB_FILE="${1:-}"
|
||||
if [ -z "$JOB_FILE" ] || [ ! -f "$JOB_FILE" ]; then
|
||||
echo "borg-restore: missing job file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
JOB_BASE="$(basename "$JOB_FILE")"
|
||||
JOB_ID="${JOB_BASE%%.*}"
|
||||
|
||||
PLUGIN_DIR="/usr/local/directadmin/plugins/borg-restore"
|
||||
LOG_DIR="${PLUGIN_DIR}/data/logs"
|
||||
LOG_FILE="${LOG_DIR}/${JOB_ID}.log"
|
||||
|
||||
mkdir -p "$LOG_DIR"
|
||||
|
||||
fail() {
|
||||
local msg="$1"
|
||||
ERROR_MSG="$msg"
|
||||
echo "$msg" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
format_restore_path() {
|
||||
local path="${1:-}"
|
||||
path="$(echo "$path" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
|
||||
if [ -z "$path" ]; then
|
||||
echo ""
|
||||
return
|
||||
fi
|
||||
if [[ "$path" != /* ]]; then
|
||||
path="/$path"
|
||||
fi
|
||||
echo "$path"
|
||||
}
|
||||
|
||||
append_missing_include_explanation() {
|
||||
if [ ! -f "$LOG_FILE" ]; then
|
||||
return
|
||||
fi
|
||||
if ! grep -qi "Include pattern '.*' never matched" "$LOG_FILE"; then
|
||||
return
|
||||
fi
|
||||
if grep -Fq "W przywracanej kopii zapasowej nie odnaleziono wskazanej ścieżki" "$LOG_FILE" || grep -Fq "The specified restore path was not found in the backup" "$LOG_FILE"; then
|
||||
return
|
||||
fi
|
||||
local label
|
||||
label="$(format_restore_path "${USERRESTOREPATH:-}")"
|
||||
local msg
|
||||
if [ "${USERLANG:-}" = "en" ]; then
|
||||
msg="The specified restore path"
|
||||
else
|
||||
msg="W przywracanej kopii zapasowej nie odnaleziono wskazanej ścieżki"
|
||||
fi
|
||||
if [ -n "$label" ]; then
|
||||
msg+=" (${label})"
|
||||
fi
|
||||
if [ "${USERLANG:-}" = "en" ]; then
|
||||
msg+=" was not found in the backup. Please try restoring from another archive that may contain those files."
|
||||
else
|
||||
msg+=". Prosimy spróbować przywrócić dane z innego archiwum, w którym te pliki mogły się znajdować."
|
||||
fi
|
||||
printf "\n%s\n" "$msg" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
has_missing_include() {
|
||||
if [ ! -f "$LOG_FILE" ]; then
|
||||
return 1
|
||||
fi
|
||||
grep -qi "Include pattern '.*' never matched" "$LOG_FILE"
|
||||
}
|
||||
|
||||
filter_log_content() {
|
||||
awk '{
|
||||
line=tolower($0);
|
||||
if (line ~ /permanently added/) next;
|
||||
if (line ~ /known host/) next;
|
||||
print;
|
||||
}'
|
||||
}
|
||||
|
||||
secure_source_check() {
|
||||
local path="$1"
|
||||
local label="$2"
|
||||
local check_writable="${3:-1}"
|
||||
if [ -z "$path" ] || [ ! -e "$path" ]; then
|
||||
fail "Missing ${label}: ${path}"
|
||||
exit 1
|
||||
fi
|
||||
if [ -L "$path" ] || [ ! -f "$path" ]; then
|
||||
fail "Insecure ${label}: not a regular file (${path})"
|
||||
exit 1
|
||||
fi
|
||||
local owner_uid owner_name mode
|
||||
owner_uid="$(stat -c %u "$path" 2>/dev/null || true)"
|
||||
owner_name="$(stat -c %U "$path" 2>/dev/null || true)"
|
||||
mode="$(stat -c %a "$path" 2>/dev/null || true)"
|
||||
if [ -z "$owner_uid" ] || [ -z "$mode" ]; then
|
||||
fail "Unable to stat ${label}: ${path}"
|
||||
exit 1
|
||||
fi
|
||||
if [ "$owner_uid" != "0" ] && [ "$owner_uid" != "500" ] && [ "$owner_name" != "root" ] && [ "$owner_name" != "diradmin" ]; then
|
||||
fail "Insecure ${label} owner (${owner_name}:${owner_uid}): ${path}"
|
||||
exit 1
|
||||
fi
|
||||
mode=$((10#$mode))
|
||||
if [ "$check_writable" = "1" ] && (( (mode & 022) != 0 )); then
|
||||
fail "Insecure ${label} permissions (group/other writable): ${path}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
urlencode() {
|
||||
local input="$1"
|
||||
local hex out i
|
||||
hex="$(printf '%s' "$input" | od -An -tx1 | tr -d ' \n')"
|
||||
out=""
|
||||
for ((i=0; i<${#hex}; i+=2)); do
|
||||
out+="%${hex:i:2}"
|
||||
done
|
||||
printf '%s' "$out"
|
||||
}
|
||||
|
||||
notify_user() {
|
||||
local code="$1"
|
||||
if [ -z "${DA_USER:-}" ]; then
|
||||
return
|
||||
fi
|
||||
local subject message
|
||||
if [ "$code" -eq 0 ] && [ "$RESTORE_OK" -eq 1 ]; then
|
||||
if [ "${USERLANG:-}" = "en" ]; then
|
||||
subject="BorgBackup restore completed successfully"
|
||||
message="Restore of ${USERRESTOREPATH:-} from ${REPONAME:-} completed."
|
||||
else
|
||||
subject="Przywracanie plików BorgBackup zakończone pomyślnie"
|
||||
message="${SUCCESS_MSG:-Przywracanie ${USERRESTOREPATH:-} z ${REPONAME:-} zakończone.}"
|
||||
fi
|
||||
else
|
||||
if [ "${USERLANG:-}" = "en" ]; then
|
||||
subject="BorgBackup restore error"
|
||||
message="Restore failed for ${USERRESTOREPATH:-} from ${REPONAME:-}."
|
||||
else
|
||||
subject="Błąd przywracania plików BorgBackup"
|
||||
if [ -n "$ERROR_MSG" ]; then
|
||||
message="Błąd przywracania: ${ERROR_MSG}"
|
||||
else
|
||||
message="Błąd przywracania ${USERRESTOREPATH:-} z ${REPONAME:-}."
|
||||
fi
|
||||
fi
|
||||
if [ -n "${LOG_FILE:-}" ] && [ -f "$LOG_FILE" ]; then
|
||||
local log_content
|
||||
log_content="$(cat "$LOG_FILE" | filter_log_content)"
|
||||
message="${message}\n\nLog:\n${log_content}"
|
||||
fi
|
||||
fi
|
||||
local task_queue="/usr/local/directadmin/data/task.queue"
|
||||
local users="select1%3D$(urlencode "$DA_USER")"
|
||||
local line="action=notify&value=users&subject=$(urlencode "$subject")&message=$(urlencode "$message")&users=${users}"
|
||||
printf "%s\n" "$line" >> "$task_queue"
|
||||
}
|
||||
|
||||
on_exit() {
|
||||
local code="$1"
|
||||
notify_user "$code"
|
||||
}
|
||||
trap 'on_exit $?' EXIT
|
||||
|
||||
secure_source_check "$JOB_FILE" "job file" 0
|
||||
# shellcheck disable=SC1090
|
||||
source "$JOB_FILE"
|
||||
|
||||
DA_USER="${DA_USER:-}"
|
||||
if [ -z "${DA_USER:-}" ]; then
|
||||
echo "Missing DA_USER in job file" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CONFIG_FILE="${PLUGIN_DIR}/plugin-settings.conf"
|
||||
|
||||
set_owner() {
|
||||
local path="$1"
|
||||
if [ -z "$path" ] || [ ! -e "$path" ]; then
|
||||
return
|
||||
fi
|
||||
if [ "$RESTORETYPE" = "mail" ]; then
|
||||
chown -R "${DA_USER}:mail" "$path"
|
||||
else
|
||||
chown -R "${DA_USER}:${DA_USER}" "$path"
|
||||
fi
|
||||
}
|
||||
|
||||
is_true() {
|
||||
local v="${1:-}"
|
||||
v="$(printf '%s' "$v" | tr '[:upper:]' '[:lower:]')"
|
||||
case "$v" in
|
||||
1|true|yes|on) return 0 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
}
|
||||
|
||||
if [ ! -f "$CONFIG_FILE" ]; then
|
||||
fail "Missing ${CONFIG_FILE}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
secure_source_check "$CONFIG_FILE" "plugin-settings.conf"
|
||||
# shellcheck disable=SC1090
|
||||
source "$CONFIG_FILE"
|
||||
|
||||
BB_PATH="${BB_PATH:-/opt/bb}"
|
||||
BORG_PATH="${BORG_PATH:-/usr/bin/borg}"
|
||||
BORG_VARIABLES_FILE="${BORG_VARIABLES_FILE:-}"
|
||||
WEB_RESTORE_LOCATION="${WEB_RESTORE_LOCATION:-/home/DA_USER/domains}"
|
||||
MAIL_RESTORE_LOCATION="${MAIL_RESTORE_LOCATION:-/home/DA_USER/imap}"
|
||||
BLOCK_RESTORE_OUTSIDE_LOCATION="${BLOCK_RESTORE_OUTSIDE_LOCATION:-TRUE}"
|
||||
CLEAR_DOVECOT_INDEXES_AFTER_MERGE="${CLEAR_DOVECOT_INDEXES_AFTER_MERGE:-TRUE}"
|
||||
RESTORE_PATH_MODE="${RESTORE_PATH_MODE:-dir}"
|
||||
|
||||
if [ -z "$BB_PATH" ]; then
|
||||
fail "BB_PATH is empty in plugin-settings.conf"
|
||||
exit 1
|
||||
fi
|
||||
if [ -z "$BORG_PATH" ]; then
|
||||
fail "BORG_PATH is empty in plugin-settings.conf"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
BBVARS_PATH="${BORG_VARIABLES_FILE:-}"
|
||||
BBVARS_PATH="${BBVARS_PATH//DA_USER/${DA_USER}}"
|
||||
if [ -z "$BBVARS_PATH" ]; then
|
||||
BBVARS_PATH="$BB_PATH"
|
||||
if [[ "$BBVARS_PATH" != *.sh ]]; then
|
||||
BBVARS_PATH="${BBVARS_PATH%/}/bbvars.sh"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f "$BBVARS_PATH" ]; then
|
||||
fail "Missing ${BBVARS_PATH}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
secure_source_check "$BBVARS_PATH" "bbvars.sh" 0
|
||||
set -a
|
||||
# shellcheck disable=SC1091
|
||||
source "$BBVARS_PATH"
|
||||
set +a
|
||||
|
||||
if [ -z "${REPO:-}" ]; then
|
||||
fail "Missing REPO in ${BBVARS_PATH}"
|
||||
exit 1
|
||||
fi
|
||||
if [ -n "${BORG_SSH_KEY:-}" ]; then
|
||||
if [ ! -f "${BORG_SSH_KEY}" ]; then
|
||||
fail "BORG_SSH_KEY is not a file: ${BORG_SSH_KEY}"
|
||||
exit 1
|
||||
fi
|
||||
export BORG_RSH="ssh -i \"${BORG_SSH_KEY}\""
|
||||
fi
|
||||
|
||||
if [ -z "${REPONAME:-}" ] || [ -z "${USERRESTOREPATH:-}" ]; then
|
||||
fail "Missing REPONAME or USERRESTOREPATH in job file"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
RESTOREMODE="${RESTOREMODE:-location}"
|
||||
RESTORETYPE="${RESTORETYPE:-web}"
|
||||
if [ "$RESTORETYPE" != "mail" ]; then
|
||||
RESTORETYPE="web"
|
||||
fi
|
||||
if [ "$RESTORETYPE" = "mail" ]; then
|
||||
if [ "$RESTOREMODE" != "location" ] && [ "$RESTOREMODE" != "update" ]; then
|
||||
RESTOREMODE="update"
|
||||
fi
|
||||
else
|
||||
if [ "$RESTOREMODE" != "location" ] && [ "$RESTOREMODE" != "overwrite" ]; then
|
||||
RESTOREMODE="location"
|
||||
fi
|
||||
fi
|
||||
|
||||
RESTOREPATHMODE="${RESTOREPATHMODE:-$RESTORE_PATH_MODE}"
|
||||
RESTOREPATHMODE="$(echo "$RESTOREPATHMODE" | tr '[:upper:]' '[:lower:]')"
|
||||
RESTOREPATHMODE_FLAG="full"
|
||||
if [ "$RESTOREPATHMODE" = "dir" ]; then
|
||||
RESTOREPATHMODE_FLAG="dir"
|
||||
fi
|
||||
|
||||
TARGETPATH="${TARGETPATH:-}"
|
||||
RESTORE_FULL="/${USERRESTOREPATH#/}"
|
||||
WEB_BASE="${WEB_RESTORE_LOCATION//DA_USER/$DA_USER}"
|
||||
MAIL_BASE="${MAIL_RESTORE_LOCATION//DA_USER/$DA_USER}"
|
||||
BASE_LOCATION="$WEB_BASE"
|
||||
if [ "$RESTORETYPE" = "mail" ]; then
|
||||
BASE_LOCATION="$MAIL_BASE"
|
||||
fi
|
||||
if [ -z "$BASE_LOCATION" ]; then
|
||||
fail "Base restore location is empty"
|
||||
exit 1
|
||||
fi
|
||||
TARGET_DIR="$BASE_LOCATION"
|
||||
if [ -z "$TARGETPATH" ]; then
|
||||
TARGETPATH="$TARGET_DIR"
|
||||
fi
|
||||
|
||||
BLOCK_FLAG="$(echo "$BLOCK_RESTORE_OUTSIDE_LOCATION" | tr '[:upper:]' '[:lower:]')"
|
||||
if [ "$BLOCK_FLAG" = "1" ] || [ "$BLOCK_FLAG" = "true" ] || [ "$BLOCK_FLAG" = "yes" ] || [ "$BLOCK_FLAG" = "on" ]; then
|
||||
if [ -n "$BASE_LOCATION" ]; then
|
||||
BASE_LOCATION="${BASE_LOCATION%/}"
|
||||
if [[ "$RESTORE_FULL" != "$BASE_LOCATION" && "$RESTORE_FULL" != "$BASE_LOCATION/"* ]]; then
|
||||
fail "Restore path outside base location: ${RESTORE_FULL}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ "$USERRESTOREPATH" == *".."* ]]; then
|
||||
fail "Invalid restore path (contains ..)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ "$TARGETPATH" == *".."* ]]; then
|
||||
fail "Invalid target path (contains ..)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$RESTOREMODE" = "overwrite" ] || [ "$RESTOREMODE" = "update" ]; then
|
||||
TMP_BASE="/home/${DA_USER}/bb.tmp"
|
||||
RESTORE_BASE="${RESTORE_FULL##*/}"
|
||||
RESTORE_PARENT="${RESTORE_FULL%/*}"
|
||||
|
||||
if [ -z "$RESTORE_BASE" ] || [ -z "$RESTORE_PARENT" ]; then
|
||||
fail "Nieprawidłowa ścieżka do przywrócenia."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$TMP_BASE"
|
||||
chown -R "${DA_USER}:${DA_USER}" "$TMP_BASE"
|
||||
|
||||
rm -rf "${TMP_BASE}/${USERRESTOREPATH#/}" "${TMP_BASE}/${RESTORE_BASE}" 2>/dev/null || true
|
||||
|
||||
cd "$TMP_BASE"
|
||||
set +e
|
||||
"$BORG_PATH" extract --progress "${REPO}::${REPONAME}" -- "${USERRESTOREPATH}" >> "$LOG_FILE" 2>&1
|
||||
EXIT_CODE=$?
|
||||
set -e
|
||||
append_missing_include_explanation
|
||||
|
||||
if [ "$EXIT_CODE" -ne 0 ]; then
|
||||
if ! has_missing_include; then
|
||||
fail "Borg extract zakończył się błędem. Sprawdź logi w tabeli zdarzeń na stronie pluginu."
|
||||
fi
|
||||
else
|
||||
EXTRACTED_PATH="${TMP_BASE}/${USERRESTOREPATH#/}"
|
||||
TMP_ITEM="${TMP_BASE}/${RESTORE_BASE}"
|
||||
if [ ! -e "$EXTRACTED_PATH" ]; then
|
||||
fail "Nie znaleziono przywróconego katalogu: ${EXTRACTED_PATH}"
|
||||
EXIT_CODE=1
|
||||
else
|
||||
if [ -e "$TMP_ITEM" ]; then
|
||||
rm -rf "$TMP_ITEM" 2>/dev/null || true
|
||||
fi
|
||||
mv "$EXTRACTED_PATH" "$TMP_ITEM"
|
||||
|
||||
if [ "$RESTOREMODE" = "update" ]; then
|
||||
if [ -e "$RESTORE_FULL" ]; then
|
||||
PRE_SUFFIX="$(date +%d-%m-%Y_%H%M%S)"
|
||||
PRE_NAME="${RESTORE_BASE}_przed_przywroceniem_${PRE_SUFFIX}"
|
||||
PRE_PATH="${RESTORE_PARENT}/${PRE_NAME}"
|
||||
mkdir -p "$PRE_PATH"
|
||||
set +e
|
||||
rsync -a "$RESTORE_FULL"/ "$PRE_PATH"/ >> "$LOG_FILE" 2>&1
|
||||
RSYNC_CODE=$?
|
||||
set -e
|
||||
if [ "$RSYNC_CODE" -ne 0 ]; then
|
||||
fail "Backup katalogu przed aktualizacją zakończył się błędem."
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
else
|
||||
mkdir -p "$RESTORE_FULL"
|
||||
fi
|
||||
if [ "$EXIT_CODE" -eq 0 ]; then
|
||||
RSYNC_EXCLUDES=()
|
||||
if [ "$RESTORETYPE" = "mail" ]; then
|
||||
RSYNC_EXCLUDES+=(--exclude 'dovecot.index*')
|
||||
RSYNC_EXCLUDES+=(--exclude 'dovecot-uidlist')
|
||||
RSYNC_EXCLUDES+=(--exclude 'dovecot-uidvalidity')
|
||||
RSYNC_EXCLUDES+=(--exclude 'dovecot.list.index*')
|
||||
RSYNC_EXCLUDES+=(--exclude 'dovecot.list.log')
|
||||
fi
|
||||
set +e
|
||||
rsync -a "${RSYNC_EXCLUDES[@]}" "$TMP_ITEM"/ "$RESTORE_FULL"/ >> "$LOG_FILE" 2>&1
|
||||
RSYNC_CODE=$?
|
||||
set -e
|
||||
if [ "$RSYNC_CODE" -ne 0 ]; then
|
||||
fail "Aktualizacja danych przez rsync zakończyła się błędem."
|
||||
EXIT_CODE=1
|
||||
else
|
||||
if [ "$RESTORETYPE" = "mail" ] && is_true "$CLEAR_DOVECOT_INDEXES_AFTER_MERGE"; then
|
||||
find "$RESTORE_FULL" -type f \( -name 'dovecot.index*' -o -name 'dovecot-uidlist' -o -name 'dovecot-uidvalidity' -o -name 'dovecot.list.index*' -o -name 'dovecot.list.log' \) -delete >> "$LOG_FILE" 2>&1 || true
|
||||
fi
|
||||
set_owner "$RESTORE_FULL"
|
||||
rm -rf "$TMP_BASE"
|
||||
RESTORE_OK=1
|
||||
SUCCESS_MSG="Przywracanie ${USERRESTOREPATH} z ${REPONAME} zakończone. Pliki w ${RESTORE_FULL}."
|
||||
echo "$SUCCESS_MSG" >> "$LOG_FILE"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ -e "$RESTORE_FULL" ]; then
|
||||
PRE_SUFFIX="$(date +%d-%m-%Y_%H%M%S)"
|
||||
PRE_NAME="${RESTORE_BASE}_przed_przywroceniem_${PRE_SUFFIX}"
|
||||
PRE_PATH="${RESTORE_PARENT}/${PRE_NAME}"
|
||||
mv "$RESTORE_FULL" "$PRE_PATH"
|
||||
fi
|
||||
|
||||
mkdir -p "$RESTORE_PARENT"
|
||||
mv "$TMP_ITEM" "$RESTORE_FULL"
|
||||
set_owner "$RESTORE_FULL"
|
||||
rm -rf "$TMP_BASE"
|
||||
RESTORE_OK=1
|
||||
SUCCESS_MSG="Przywracanie ${USERRESTOREPATH} z ${REPONAME} zakończone. Pliki w ${RESTORE_FULL}."
|
||||
echo "$SUCCESS_MSG" >> "$LOG_FILE"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
else
|
||||
if [ ! -d "$TARGETPATH" ]; then
|
||||
mkdir -p "$TARGETPATH"
|
||||
fi
|
||||
if [ -d "$TARGETPATH" ] && [ "$(ls -A "$TARGETPATH" 2>/dev/null)" != "" ]; then
|
||||
fail "Target directory is not empty: ${TARGETPATH}"
|
||||
exit 1
|
||||
fi
|
||||
set_owner "$TARGETPATH"
|
||||
|
||||
TARGET_DIR="$TARGETPATH"
|
||||
|
||||
if [ "$RESTOREPATHMODE_FLAG" = "dir" ]; then
|
||||
IFS='/' read -r -a RESTORE_PARTS <<< "${USERRESTOREPATH#/}"
|
||||
PARTS_COUNT="${#RESTORE_PARTS[@]}"
|
||||
STRIP_COUNT=0
|
||||
if [ "$PARTS_COUNT" -gt 0 ]; then
|
||||
STRIP_COUNT=$((PARTS_COUNT - 1))
|
||||
fi
|
||||
|
||||
cd "$TARGET_DIR"
|
||||
set +e
|
||||
"$BORG_PATH" extract --progress --strip-components "$STRIP_COUNT" "${REPO}::${REPONAME}" -- "${USERRESTOREPATH}" >> "$LOG_FILE" 2>&1
|
||||
EXIT_CODE=$?
|
||||
set -e
|
||||
append_missing_include_explanation
|
||||
|
||||
if [ "$EXIT_CODE" -ne 0 ]; then
|
||||
if ! has_missing_include; then
|
||||
fail "Borg extract zakończył się błędem. Sprawdź logi w tabeli zdarzeń na stronie pluginu."
|
||||
fi
|
||||
else
|
||||
BASE_NAME="$(basename "${USERRESTOREPATH#/}")"
|
||||
DEST_PATH="${TARGET_DIR%/}/${BASE_NAME}"
|
||||
if [ -e "$DEST_PATH" ]; then
|
||||
set_owner "$DEST_PATH"
|
||||
fi
|
||||
RESTORE_OK=1
|
||||
SUCCESS_MSG="Przywracanie ${USERRESTOREPATH} z ${REPONAME} zakończone. Pliki w ${DEST_PATH}."
|
||||
echo "$SUCCESS_MSG" >> "$LOG_FILE"
|
||||
fi
|
||||
else
|
||||
cd "$TARGET_DIR"
|
||||
|
||||
set +e
|
||||
"$BORG_PATH" extract --progress "${REPO}::${REPONAME}" -- "${USERRESTOREPATH}" >> "$LOG_FILE" 2>&1
|
||||
EXIT_CODE=$?
|
||||
set -e
|
||||
append_missing_include_explanation
|
||||
|
||||
set_owner "$TARGET_DIR"
|
||||
|
||||
if [ "$EXIT_CODE" -eq 0 ]; then
|
||||
RESTORE_OK=1
|
||||
SUCCESS_MSG="Przywracanie ${USERRESTOREPATH} z ${REPONAME} zakończone. Pliki w ${TARGET_DIR}."
|
||||
echo "$SUCCESS_MSG" >> "$LOG_FILE"
|
||||
else
|
||||
if ! has_missing_include; then
|
||||
fail "Borg extract zakończył się błędem. Sprawdź logi w tabeli zdarzeń na stronie pluginu."
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
exit "$EXIT_CODE"
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
# Safety checks to avoid deleting the wrong path.
|
||||
if [ "$(basename "$PLUGIN_DIR")" != "borg-restore" ]; then
|
||||
echo "borg-restore: safety check failed (basename is not borg-restore): $PLUGIN_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -f "$PLUGIN_DIR/plugin.conf" ]; then
|
||||
echo "borg-restore: safety check failed (plugin.conf missing): $PLUGIN_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
CRON_FILE="/etc/cron.d/borg-restore"
|
||||
if [ -f "$CRON_FILE" ]; then
|
||||
rm -f "$CRON_FILE" || true
|
||||
fi
|
||||
|
||||
echo "borg-restore: uninstall complete"
|
||||
Executable
+60
@@ -0,0 +1,60 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
umask 077
|
||||
|
||||
PLUGIN_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
DATA_DIR="$PLUGIN_DIR/data"
|
||||
JOB_DIR="$DATA_DIR/jobs/pending"
|
||||
PROCESSING_DIR="$DATA_DIR/jobs/processing"
|
||||
DONE_DIR="$DATA_DIR/jobs/done"
|
||||
LOCK_DIR="$DATA_DIR/lock"
|
||||
PID_DIR="$DATA_DIR/pids"
|
||||
CANCEL_DIR="$DATA_DIR/cancel"
|
||||
|
||||
mkdir -p "$JOB_DIR" "$PROCESSING_DIR" "$DONE_DIR" "$PID_DIR" "$CANCEL_DIR"
|
||||
|
||||
if ! mkdir "$LOCK_DIR" 2>/dev/null; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
cleanup() {
|
||||
rmdir "$LOCK_DIR" 2>/dev/null || true
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
JOB_FILE=$(ls -1 "$JOB_DIR"/*.env 2>/dev/null | head -n 1)
|
||||
if [ -z "${JOB_FILE:-}" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
BASE_NAME="$(basename "$JOB_FILE")"
|
||||
RUN_FILE="$PROCESSING_DIR/$BASE_NAME"
|
||||
chmod 600 "$JOB_FILE" 2>/dev/null || true
|
||||
mv "$JOB_FILE" "$RUN_FILE"
|
||||
chmod 600 "$RUN_FILE" 2>/dev/null || true
|
||||
JOB_ID="${BASE_NAME%.env}"
|
||||
PID_FILE="$PID_DIR/${JOB_ID}.pid"
|
||||
|
||||
set +e
|
||||
"$PLUGIN_DIR/scripts/restore.sh" "$RUN_FILE" &
|
||||
RESTORE_PID=$!
|
||||
echo "PID=${RESTORE_PID}" >> "$RUN_FILE"
|
||||
chmod 600 "$RUN_FILE" 2>/dev/null || true
|
||||
echo "${RESTORE_PID}" > "$PID_FILE"
|
||||
wait "$RESTORE_PID"
|
||||
EXIT_CODE=$?
|
||||
set -e
|
||||
|
||||
rm -f "$PID_FILE"
|
||||
|
||||
CANCEL_FLAG="${CANCEL_DIR}/${JOB_ID}.flag"
|
||||
if [ -f "$CANCEL_FLAG" ]; then
|
||||
rm -f "$CANCEL_FLAG"
|
||||
mv "$RUN_FILE" "$DONE_DIR/${BASE_NAME}.cancel"
|
||||
elif [ "$EXIT_CODE" -eq 0 ]; then
|
||||
mv "$RUN_FILE" "$DONE_DIR/${BASE_NAME}.ok"
|
||||
else
|
||||
mv "$RUN_FILE" "$DONE_DIR/${BASE_NAME}.fail"
|
||||
fi
|
||||
|
||||
exit 0
|
||||
Reference in New Issue
Block a user