Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
333 changes: 240 additions & 93 deletions runs/backup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,39 @@ OPENWBDIRNAME=${OPENWBDIRNAME:-/}
TARBASEDIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
BACKUPDIR="$OPENWBBASEDIR/data/backup"
RAMDISKDIR="$OPENWBBASEDIR/ramdisk"
TEMPDIR="$RAMDISKDIR/temp"
TEMPDIR=$(mktemp -d --tmpdir openwb_backup_XXXXXX)
LOGDIR="$OPENWBBASEDIR/data/log"
LOGFILE="$LOGDIR/backup.log"
HOMEDIR="/home/openwb"
VAR_LIB="/var/lib"

# Mosquitto DB files to monitor
DB_FILES=(
"mosquitto/mosquitto.db"
"mosquitto_local/mosquitto.db"
)

# Timeout for mosquitto DB flush
DB_TIMEOUT=5

useExtendedFilename=$1
if ((useExtendedFilename == 1)); then
# only use characters supported in most OS!
# for Win see https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares--directories--files--and-metadata
FILENAME="openWB_backup_$(date +"%Y-%m-%d_%H-%M-%S").tar"
else
FILENAME="backup.tar"
fi
FILENAMESUFFIX=".tar.gz"

{
generate_filename() {
# generate filename
# if useExtendedFilename is 1, use date and version info
# else just use "backup"
if ((useExtendedFilename == 1)); then
# only use characters supported in most OS!
# for Win see https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares--directories--files--and-metadata
FILENAME="openWB_backup_$(date +"%Y-%m-%d_%H-%M-%S")"
else
FILENAME="backup"
fi
BACKUPFILE="$BACKUPDIR/$FILENAME"
}

log_environment() {
echo "starting backup script"
echo "environment:"
echo " OPENWBBASEDIR: $OPENWBBASEDIR"
Expand All @@ -30,95 +49,223 @@ fi
echo " LOGDIR: $LOGDIR"
echo " LOGFILE: $LOGFILE"
echo " FILENAME: $FILENAME"
}

echo "deleting old backup files if present in '$BACKUPDIR'"
# remove old backup files
rm -v "$BACKUPDIR/"*
BACKUPFILE="$BACKUPDIR/$FILENAME"
remove_old_backups() {
echo "removing old files in '$BACKUPDIR' if present"
# Delete old backups (robust, no glob issues)
find "$BACKUPDIR" -mindepth 1 -maxdepth 1 -not -name '.donotdelete' -exec rm -vrf {} +
}

force_mosquitto_write() {
collect_baseline() {
echo "collecting Baseline-mtime before SIGUSR1:"
# Baseline mtimes to avoid race condition (captured before SIGUSR1)
declare -Ag BASELINE_DB_MTIME=()

for f in "${DB_FILES[@]}"; do
if [ -e "$VAR_LIB/$f" ]; then
BASELINE_DB_MTIME["$f"]=$(stat -c %Y "$VAR_LIB/$f")
echo " $f -> ${BASELINE_DB_MTIME["$f"]}"
else
BASELINE_DB_MTIME["$f"]=0
echo " $f -> (does not yet exist, setting to 0)"
fi
done
}

flush_mosquitto() {
# inform mosquitto to flush data to disk
echo "sending 'SIGUSR1' to mosquitto processes to flush data to disk"
sudo pkill -e -SIGUSR1 mosquitto || echo "WARNING: no processes found?"
}

wait_for_mosquitto_flush() {
# wait for mosquitto to flush db files to disk
# uses inotifywait if available, else falls back to polling
# requires inotify-tools package for inotifywait
local start_ts
start_ts=$(date +%s)
declare -A initial_mtime

# Use previously captured baseline (recorded before signal)
if ((${#BASELINE_DB_MTIME[@]})); then
for f in "${DB_FILES[@]}"; do
initial_mtime["$VAR_LIB/$f"]=${BASELINE_DB_MTIME["$f"]:-0}
done
else
# Fallback (should not occur)
for f in "${DB_FILES[@]}"; do
[ -e "$VAR_LIB/$f" ] && initial_mtime["$VAR_LIB/$f"]=$(stat -c %Y "$VAR_LIB/$f") || initial_mtime["$VAR_LIB/$f"]=0
done
fi

# tell mosquitto to store all retained topics in db now
echo "sending 'SIGUSR1' to mosquitto processes"
sudo pkill -e -SIGUSR1 mosquitto
# give mosquitto some time to finish
sleep 1

# create backup file
echo "creating new backup file: $BACKUPFILE"
echo "adding openWB files"
tar --verbose --create \
--file="$BACKUPFILE" \
--directory="$TARBASEDIR/" \
"$OPENWBDIRNAME/data/charge_log" \
"$OPENWBDIRNAME/data/daily_log" \
"$OPENWBDIRNAME/data/monthly_log" \
"$OPENWBDIRNAME/data/log/uuid"
echo "adding configuration file"
sudo tar --verbose --append \
--file="$BACKUPFILE" \
--directory="/home/openwb/" \
"configuration.json"
echo "adding mosquitto files"
sudo tar --verbose --append \
--file="$BACKUPFILE" \
--directory="/var/lib/" \
"mosquitto/" "mosquitto_local/"
echo "adding mosquitto configuration"
sudo tar --verbose --append \
--file="$BACKUPFILE" \
--directory="/etc/mosquitto/" \
"conf_local.d/"
echo "adding boot file"
sudo tar --verbose --append \
--file="$BACKUPFILE" \
"/boot/config.txt"
echo "adding git information"
git branch --no-color --show-current >"$RAMDISKDIR/GIT_BRANCH"
git log --pretty='format:%H' -n1 >"$RAMDISKDIR/GIT_HASH"
echo "branch: $(<"$RAMDISKDIR/GIT_BRANCH") commit-hash: $(<"$RAMDISKDIR/GIT_HASH")"
tar --verbose --append \
--file="$BACKUPFILE" \
--directory="$RAMDISKDIR/" \
"GIT_BRANCH" "GIT_HASH"

echo "calculating checksums"
IFS=$'\n'
mapfile -t file_list < <(tar -tf "$BACKUPFILE")
mkdir -p "$TEMPDIR"
# process each file
for file in "${file_list[@]}"; do
# skip directories
if [[ $file =~ /$ ]]; then
echo "skipping directory $file"
continue
echo "waiting for mosquitto to flush db files (timeout ${DB_TIMEOUT}s)..."

if command -v inotifywait >/dev/null 2>&1; then
echo "using 'inotifywait'"
local pending=()
for f in "${DB_FILES[@]}"; do
pending+=("$VAR_LIB/$f")
done
while ((${#pending[@]})); do
local elapsed=$(( $(date +%s) - start_ts ))
local remain=$(( DB_TIMEOUT - elapsed ))
(( remain <= 0 )) && echo "timeout reached (inotify), continuing." && break
inotifywait -q -t "$remain" -e modify -e close_write -e attrib -e move -e create "${pending[@]}" 2>/dev/null || true
local new_pending=()
for f in "${pending[@]}"; do
if [ -e "$f" ]; then
local mt
mt=$(stat -c %Y "$f")
if (( mt != initial_mtime["$f"] )); then
echo "modified: $f (old: ${initial_mtime["$f"]} -> new: $mt)"
else
new_pending+=("$f")
fi
else
new_pending+=("$f")
fi
done
pending=("${new_pending[@]}")
done
else
echo "'inotifywait' not found, falling back to polling"
while true; do
local all_ok=1
for f in "${DB_FILES[@]}"; do
if [ -e "$VAR_LIB/$f" ]; then
local mt
mt=$(stat -c %Y "$VAR_LIB/$f")
if (( mt != initial_mtime["$VAR_LIB/$f"] )); then
echo "modified (polling): $f (old: ${initial_mtime["$VAR_LIB/$f"]} -> new: $mt)"
initial_mtime["$VAR_LIB/$f"]=$mt
else
all_ok=0
fi
else
all_ok=0
fi
done
local elapsed=$(( $(date +%s) - start_ts ))
(( all_ok == 1 )) && echo "all relevant files are modified after ${elapsed}s" && break
(( elapsed >= DB_TIMEOUT )) && echo "timeout reached (polling), continuing." && break
sleep 0.1
done
fi
# extract the file
tar -xf "$BACKUPFILE" -C "$TEMPDIR" "$file"
# calculate the checksum
sha256sum "$TEMPDIR/$file" | sed -n "s|$TEMPDIR/||p" >> "$RAMDISKDIR/SHA256SUM"
# remove the file
rm -f "$TEMPDIR/$file"
done
tar --verbose --append \
--file="$BACKUPFILE" \
--directory="$RAMDISKDIR/" \
"SHA256SUM"

# cleanup
echo "removing temporary files"
rm -v "$RAMDISKDIR/GIT_BRANCH" "$RAMDISKDIR/GIT_HASH" "$RAMDISKDIR/SHA256SUM"
rm -rf "${TEMPDIR:?}/"
tar --append \
--file="$BACKUPFILE" \
--directory="$LOGDIR/" \
"backup.log"
echo "zipping archive"
gzip --verbose "$BACKUPFILE"
echo "setting permissions of new backup file"
sudo chown openwb:www-data "$BACKUPFILE.gz"
sudo chmod 664 "$BACKUPFILE.gz"
}

copy_db_to_temp() {
echo "copying mosquitto db files to temporary location"
for f in "${DB_FILES[@]}"; do
if [ -e "$VAR_LIB/$f" ]; then
local dest_dir
dest_dir="$TEMPDIR/$(dirname "$f")"
mkdir -p "$dest_dir"
sudo cp -v "$VAR_LIB/$f" "$dest_dir/"
else
echo "skipping $f (does not exist)"
fi
done
}

collect_baseline
flush_mosquitto
wait_for_mosquitto_flush
copy_db_to_temp
}

collect_git_info() {
echo "collecting git information"
git branch --no-color --show-current >"$TEMPDIR/GIT_BRANCH"
git log --pretty='format:%H' -n1 >"$TEMPDIR/GIT_HASH"
echo "branch: $(<"$TEMPDIR/GIT_BRANCH") commit-hash: $(<"$TEMPDIR/GIT_HASH")"
}

create_archive() {
create_backup() {
echo "creating new backup file: $BACKUPFILE"
echo "adding files"

sudo tar --verbose --create \
--file="$BACKUPFILE" \
--exclude=".gitignore" \
--directory="$TEMPDIR/" \
"${DB_FILES[@]}" \
"GIT_BRANCH" \
"GIT_HASH" \
--directory="/etc/mosquitto/" \
"conf_local.d/" \
--directory="$TARBASEDIR/" \
"$OPENWBDIRNAME/data/charge_log" \
"$OPENWBDIRNAME/data/daily_log" \
"$OPENWBDIRNAME/data/monthly_log" \
"$OPENWBDIRNAME/data/log/uuid" \
--directory="$HOMEDIR/" \
"configuration.json" \
--directory="/" \
"boot/config.txt"
}

calculate_checksums() {
echo "calculating checksums"
IFS=$'\n'
mapfile -t file_list < <(tar -tf "$BACKUPFILE")
# process each file
for file in "${file_list[@]}"; do
# skip directories
if [[ $file =~ /$ ]]; then
echo "skipping directory $file"
continue
fi
# extract the file
tar -xf "$BACKUPFILE" -C "$TEMPDIR" "$file"
# calculate the checksum
sha256sum "$TEMPDIR/$file" | sed -n "s|$TEMPDIR/||p" >> "$TEMPDIR/SHA256SUM"
# remove the file
rm -f "$TEMPDIR/$file"
done

echo "adding checksum file to archive"
sudo tar --verbose --append \
--file="$BACKUPFILE" \
--directory="$TEMPDIR/" \
"SHA256SUM"
}

cleanup_and_compress() {
echo "removing temporary files"
rm -rf "${TEMPDIR:?}/"
echo "adding log file to archive"
sudo tar --append \
--file="$BACKUPFILE" \
--directory="$LOGDIR/" \
"backup.log"
echo "zipping archive"
gzip --verbose --suffix "$FILENAMESUFFIX" "$BACKUPFILE"
}

fix_permissions() {
echo "setting permissions of new backup file"
sudo chown openwb:www-data "$BACKUPFILE$FILENAMESUFFIX"
sudo chmod 664 "$BACKUPFILE$FILENAMESUFFIX"
}

create_backup
calculate_checksums
cleanup_and_compress
fix_permissions
}

{
generate_filename
log_environment
remove_old_backups
collect_git_info
force_mosquitto_write
create_archive
echo "backup finished"
} >"$LOGFILE" 2>&1

# return our filename for further processing
echo "$FILENAME.gz"
echo "$FILENAME$FILENAMESUFFIX"
2 changes: 1 addition & 1 deletion runs/install_packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
echo "install required packages with 'apt-get'..."
sudo apt-get -q update
sudo apt-get -q -y install \
vim bc jq socat sshpass sudo ssl-cert mmc-utils \
vim bc jq socat sshpass sudo ssl-cert mmc-utils inotify-tools \
apache2 libapache2-mod-php \
php php-gd php-curl php-xml php-json \
git \
Expand Down
4 changes: 2 additions & 2 deletions web/settings/uploadFile.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ function check_gzip() {
// quick check for file contents
function check_restore_file_contents() {
$output = null;
$result = exec("tar --list --file=\"" . $_FILES["file"]["tmp_name"] . "\" | grep -c \"^\(mosquitto/\|mosquitto_local/\|GIT_HASH\|GIT_BRANCH\|SHA256SUM\|backup.log\)$\"", $output);
$result = exec("tar --list --file=\"" . $_FILES["file"]["tmp_name"] . "\" | egrep -c \"^(mosquitto/.+|mosquitto_local/.+|GIT_HASH|GIT_BRANCH|SHA256SUM|backup.log)$\"", $output);
if ($result === false || $result != "6") {
exit_with_error("Prüfung des Archivinhalts fehlgeschlagen! Das Archiv ist unvollständig.");
}
$result = exec("tar --list --file=\"" . $_FILES["file"]["tmp_name"] . "\" | grep -c \"^openWB/.*\"", $output);
$result = exec("tar --list --file=\"" . $_FILES["file"]["tmp_name"] . "\" | grep -c \"^openWB/\"", $output);
if ($result === false || $result <= 4) {
exit_with_error("Prüfung des Archivinhalts fehlgeschlagen! Das Archiv ist unvollständig.");
}
Expand Down