@@ -5,20 +5,39 @@ OPENWBDIRNAME=${OPENWBDIRNAME:-/}
55TARBASEDIR=$( cd " $( dirname " ${BASH_SOURCE[0]} " ) /../.." && pwd)
66BACKUPDIR=" $OPENWBBASEDIR /data/backup"
77RAMDISKDIR=" $OPENWBBASEDIR /ramdisk"
8- TEMPDIR=" $RAMDISKDIR /temp "
8+ TEMPDIR=$( mktemp -d --tmpdir openwb_backup_XXXXXX )
99LOGDIR=" $OPENWBBASEDIR /data/log"
1010LOGFILE=" $LOGDIR /backup.log"
11+ HOMEDIR=" /home/openwb"
12+ VAR_LIB=" /var/lib"
13+
14+ # Mosquitto DB files to monitor
15+ DB_FILES=(
16+ " mosquitto/mosquitto.db"
17+ " mosquitto_local/mosquitto.db"
18+ )
19+
20+ # Timeout for mosquitto DB flush
21+ DB_TIMEOUT=5
1122
1223useExtendedFilename=$1
13- if (( useExtendedFilename == 1 )) ; then
14- # only use characters supported in most OS!
15- # for Win see https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares--directories--files--and-metadata
16- FILENAME=" openWB_backup_$( date +" %Y-%m-%d_%H-%M-%S" ) .tar"
17- else
18- FILENAME=" backup.tar"
19- fi
24+ FILENAMESUFFIX=" .tar.gz"
2025
21- {
26+ generate_filename () {
27+ # generate filename
28+ # if useExtendedFilename is 1, use date and version info
29+ # else just use "backup"
30+ if (( useExtendedFilename == 1 )) ; then
31+ # only use characters supported in most OS!
32+ # for Win see https://learn.microsoft.com/en-us/rest/api/storageservices/naming-and-referencing-shares--directories--files--and-metadata
33+ FILENAME=" openWB_backup_$( date +" %Y-%m-%d_%H-%M-%S" ) "
34+ else
35+ FILENAME=" backup"
36+ fi
37+ BACKUPFILE=" $BACKUPDIR /$FILENAME "
38+ }
39+
40+ log_environment () {
2241 echo " starting backup script"
2342 echo " environment:"
2443 echo " OPENWBBASEDIR: $OPENWBBASEDIR "
3049 echo " LOGDIR: $LOGDIR "
3150 echo " LOGFILE: $LOGFILE "
3251 echo " FILENAME: $FILENAME "
52+ }
3353
34- echo " deleting old backup files if present in '$BACKUPDIR '"
35- # remove old backup files
36- rm -v " $BACKUPDIR /" *
37- BACKUPFILE=" $BACKUPDIR /$FILENAME "
54+ remove_old_backups () {
55+ echo " removing old files in '$BACKUPDIR ' if present"
56+ # Delete old backups (robust, no glob issues)
57+ find " $BACKUPDIR " -mindepth 1 -maxdepth 1 -not -name ' .donotdelete' -exec rm -vrf {} +
58+ }
59+
60+ force_mosquitto_write () {
61+ collect_baseline () {
62+ echo " collecting Baseline-mtime before SIGUSR1:"
63+ # Baseline mtimes to avoid race condition (captured before SIGUSR1)
64+ declare -Ag BASELINE_DB_MTIME=()
65+
66+ for f in " ${DB_FILES[@]} " ; do
67+ if [ -e " $VAR_LIB /$f " ]; then
68+ BASELINE_DB_MTIME[" $f " ]=$( stat -c %Y " $VAR_LIB /$f " )
69+ echo " $f -> ${BASELINE_DB_MTIME["$f"]} "
70+ else
71+ BASELINE_DB_MTIME[" $f " ]=0
72+ echo " $f -> (does not yet exist, setting to 0)"
73+ fi
74+ done
75+ }
76+
77+ flush_mosquitto () {
78+ # inform mosquitto to flush data to disk
79+ echo " sending 'SIGUSR1' to mosquitto processes to flush data to disk"
80+ sudo pkill -e -SIGUSR1 mosquitto || echo " WARNING: no processes found?"
81+ }
82+
83+ wait_for_mosquitto_flush () {
84+ # wait for mosquitto to flush db files to disk
85+ # uses inotifywait if available, else falls back to polling
86+ # requires inotify-tools package for inotifywait
87+ local start_ts
88+ start_ts=$( date +%s)
89+ declare -A initial_mtime
90+
91+ # Use previously captured baseline (recorded before signal)
92+ if (( ${# BASELINE_DB_MTIME[@]} )) ; then
93+ for f in " ${DB_FILES[@]} " ; do
94+ initial_mtime[" $VAR_LIB /$f " ]=${BASELINE_DB_MTIME["$f"]:- 0}
95+ done
96+ else
97+ # Fallback (should not occur)
98+ for f in " ${DB_FILES[@]} " ; do
99+ [ -e " $VAR_LIB /$f " ] && initial_mtime[" $VAR_LIB /$f " ]=$( stat -c %Y " $VAR_LIB /$f " ) || initial_mtime[" $VAR_LIB /$f " ]=0
100+ done
101+ fi
38102
39- # tell mosquitto to store all retained topics in db now
40- echo " sending 'SIGUSR1' to mosquitto processes"
41- sudo pkill -e -SIGUSR1 mosquitto
42- # give mosquitto some time to finish
43- sleep 1
44-
45- # create backup file
46- echo " creating new backup file: $BACKUPFILE "
47- echo " adding openWB files"
48- tar --verbose --create \
49- --file=" $BACKUPFILE " \
50- --directory=" $TARBASEDIR /" \
51- " $OPENWBDIRNAME /data/charge_log" \
52- " $OPENWBDIRNAME /data/daily_log" \
53- " $OPENWBDIRNAME /data/monthly_log" \
54- " $OPENWBDIRNAME /data/log/uuid"
55- echo " adding configuration file"
56- sudo tar --verbose --append \
57- --file=" $BACKUPFILE " \
58- --directory=" /home/openwb/" \
59- " configuration.json"
60- echo " adding mosquitto files"
61- sudo tar --verbose --append \
62- --file=" $BACKUPFILE " \
63- --directory=" /var/lib/" \
64- " mosquitto/" " mosquitto_local/"
65- echo " adding mosquitto configuration"
66- sudo tar --verbose --append \
67- --file=" $BACKUPFILE " \
68- --directory=" /etc/mosquitto/" \
69- " conf_local.d/"
70- echo " adding boot file"
71- sudo tar --verbose --append \
72- --file=" $BACKUPFILE " \
73- " /boot/config.txt"
74- echo " adding git information"
75- git branch --no-color --show-current > " $RAMDISKDIR /GIT_BRANCH"
76- git log --pretty=' format:%H' -n1 > " $RAMDISKDIR /GIT_HASH"
77- echo " branch: $( < " $RAMDISKDIR /GIT_BRANCH" ) commit-hash: $( < " $RAMDISKDIR /GIT_HASH" ) "
78- tar --verbose --append \
79- --file=" $BACKUPFILE " \
80- --directory=" $RAMDISKDIR /" \
81- " GIT_BRANCH" " GIT_HASH"
82-
83- echo " calculating checksums"
84- IFS=$' \n '
85- mapfile -t file_list < <( tar -tf " $BACKUPFILE " )
86- mkdir -p " $TEMPDIR "
87- # process each file
88- for file in " ${file_list[@]} " ; do
89- # skip directories
90- if [[ $file =~ /$ ]]; then
91- echo " skipping directory $file "
92- continue
103+ echo " waiting for mosquitto to flush db files (timeout ${DB_TIMEOUT} s)..."
104+
105+ if command -v inotifywait > /dev/null 2>&1 ; then
106+ echo " using 'inotifywait'"
107+ local pending=()
108+ for f in " ${DB_FILES[@]} " ; do
109+ pending+=(" $VAR_LIB /$f " )
110+ done
111+ while (( ${# pending[@]} )) ; do
112+ local elapsed=$(( $(date +% s) - start_ts ))
113+ local remain=$(( DB_TIMEOUT - elapsed ))
114+ (( remain <= 0 )) && echo " timeout reached (inotify), continuing." && break
115+ inotifywait -q -t " $remain " -e modify -e close_write -e attrib -e move -e create " ${pending[@]} " 2> /dev/null || true
116+ local new_pending=()
117+ for f in " ${pending[@]} " ; do
118+ if [ -e " $f " ]; then
119+ local mt
120+ mt=$( stat -c %Y " $f " )
121+ if (( mt != initial_mtime["$f "] )) ; then
122+ echo " modified: $f (old: ${initial_mtime["$f"]} -> new: $mt )"
123+ else
124+ new_pending+=(" $f " )
125+ fi
126+ else
127+ new_pending+=(" $f " )
128+ fi
129+ done
130+ pending=(" ${new_pending[@]} " )
131+ done
132+ else
133+ echo " 'inotifywait' not found, falling back to polling"
134+ while true ; do
135+ local all_ok=1
136+ for f in " ${DB_FILES[@]} " ; do
137+ if [ -e " $VAR_LIB /$f " ]; then
138+ local mt
139+ mt=$( stat -c %Y " $VAR_LIB /$f " )
140+ if (( mt != initial_mtime["$VAR_LIB / $f "] )) ; then
141+ echo " modified (polling): $f (old: ${initial_mtime["$VAR_LIB/$f"]} -> new: $mt )"
142+ initial_mtime[" $VAR_LIB /$f " ]=$mt
143+ else
144+ all_ok=0
145+ fi
146+ else
147+ all_ok=0
148+ fi
149+ done
150+ local elapsed=$(( $(date +% s) - start_ts ))
151+ (( all_ok == 1 )) && echo " all relevant files are modified after ${elapsed} s" && break
152+ (( elapsed >= DB_TIMEOUT )) && echo " timeout reached (polling), continuing." && break
153+ sleep 0.1
154+ done
93155 fi
94- # extract the file
95- tar -xf " $BACKUPFILE " -C " $TEMPDIR " " $file "
96- # calculate the checksum
97- sha256sum " $TEMPDIR /$file " | sed -n " s|$TEMPDIR /||p" >> " $RAMDISKDIR /SHA256SUM"
98- # remove the file
99- rm -f " $TEMPDIR /$file "
100- done
101- tar --verbose --append \
102- --file=" $BACKUPFILE " \
103- --directory=" $RAMDISKDIR /" \
104- " SHA256SUM"
105-
106- # cleanup
107- echo " removing temporary files"
108- rm -v " $RAMDISKDIR /GIT_BRANCH" " $RAMDISKDIR /GIT_HASH" " $RAMDISKDIR /SHA256SUM"
109- rm -rf " ${TEMPDIR:? } /"
110- tar --append \
111- --file=" $BACKUPFILE " \
112- --directory=" $LOGDIR /" \
113- " backup.log"
114- echo " zipping archive"
115- gzip --verbose " $BACKUPFILE "
116- echo " setting permissions of new backup file"
117- sudo chown openwb:www-data " $BACKUPFILE .gz"
118- sudo chmod 664 " $BACKUPFILE .gz"
156+ }
157+
158+ copy_db_to_temp () {
159+ echo " copying mosquitto db files to temporary location"
160+ for f in " ${DB_FILES[@]} " ; do
161+ if [ -e " $VAR_LIB /$f " ]; then
162+ local dest_dir
163+ dest_dir=" $TEMPDIR /$( dirname " $f " ) "
164+ mkdir -p " $dest_dir "
165+ sudo cp -v " $VAR_LIB /$f " " $dest_dir /"
166+ else
167+ echo " skipping $f (does not exist)"
168+ fi
169+ done
170+ }
171+
172+ collect_baseline
173+ flush_mosquitto
174+ wait_for_mosquitto_flush
175+ copy_db_to_temp
176+ }
177+
178+ collect_git_info () {
179+ echo " collecting git information"
180+ git branch --no-color --show-current > " $TEMPDIR /GIT_BRANCH"
181+ git log --pretty=' format:%H' -n1 > " $TEMPDIR /GIT_HASH"
182+ echo " branch: $( < " $TEMPDIR /GIT_BRANCH" ) commit-hash: $( < " $TEMPDIR /GIT_HASH" ) "
183+ }
184+
185+ create_archive () {
186+ create_backup () {
187+ echo " creating new backup file: $BACKUPFILE "
188+ echo " adding files"
189+
190+ sudo tar --verbose --create \
191+ --file=" $BACKUPFILE " \
192+ --exclude=" .gitignore" \
193+ --directory=" $TEMPDIR /" \
194+ " ${DB_FILES[@]} " \
195+ " GIT_BRANCH" \
196+ " GIT_HASH" \
197+ --directory=" /etc/mosquitto/" \
198+ " conf_local.d/" \
199+ --directory=" $TARBASEDIR /" \
200+ " $OPENWBDIRNAME /data/charge_log" \
201+ " $OPENWBDIRNAME /data/daily_log" \
202+ " $OPENWBDIRNAME /data/monthly_log" \
203+ " $OPENWBDIRNAME /data/log/uuid" \
204+ --directory=" $HOMEDIR /" \
205+ " configuration.json" \
206+ --directory=" /" \
207+ " boot/config.txt"
208+ }
119209
210+ calculate_checksums () {
211+ echo " calculating checksums"
212+ IFS=$' \n '
213+ mapfile -t file_list < <( tar -tf " $BACKUPFILE " )
214+ # process each file
215+ for file in " ${file_list[@]} " ; do
216+ # skip directories
217+ if [[ $file =~ /$ ]]; then
218+ echo " skipping directory $file "
219+ continue
220+ fi
221+ # extract the file
222+ tar -xf " $BACKUPFILE " -C " $TEMPDIR " " $file "
223+ # calculate the checksum
224+ sha256sum " $TEMPDIR /$file " | sed -n " s|$TEMPDIR /||p" >> " $TEMPDIR /SHA256SUM"
225+ # remove the file
226+ rm -f " $TEMPDIR /$file "
227+ done
228+
229+ echo " adding checksum file to archive"
230+ sudo tar --verbose --append \
231+ --file=" $BACKUPFILE " \
232+ --directory=" $TEMPDIR /" \
233+ " SHA256SUM"
234+ }
235+
236+ cleanup_and_compress () {
237+ echo " removing temporary files"
238+ rm -rf " ${TEMPDIR:? } /"
239+ echo " adding log file to archive"
240+ sudo tar --append \
241+ --file=" $BACKUPFILE " \
242+ --directory=" $LOGDIR /" \
243+ " backup.log"
244+ echo " zipping archive"
245+ gzip --verbose --suffix " $FILENAMESUFFIX " " $BACKUPFILE "
246+ }
247+
248+ fix_permissions () {
249+ echo " setting permissions of new backup file"
250+ sudo chown openwb:www-data " $BACKUPFILE$FILENAMESUFFIX "
251+ sudo chmod 664 " $BACKUPFILE$FILENAMESUFFIX "
252+ }
253+
254+ create_backup
255+ calculate_checksums
256+ cleanup_and_compress
257+ fix_permissions
258+ }
259+
260+ {
261+ generate_filename
262+ log_environment
263+ remove_old_backups
264+ collect_git_info
265+ force_mosquitto_write
266+ create_archive
120267 echo " backup finished"
121268} > " $LOGFILE " 2>&1
122269
123270# return our filename for further processing
124- echo " $FILENAME .gz "
271+ echo " $FILENAME$FILENAMESUFFIX "
0 commit comments