mirror of
https://github.com/pi-hole/pi-hole
synced 2025-03-15 00:36:06 +00:00
Gravity database resilience (#5818)
This commit is contained in:
commit
9a3fcf2a85
@ -67,6 +67,11 @@ CREATE TABLE info
|
|||||||
);
|
);
|
||||||
|
|
||||||
INSERT INTO "info" VALUES('version','19');
|
INSERT INTO "info" VALUES('version','19');
|
||||||
|
/* This is a flag to indicate if gravity was restored from a backup
|
||||||
|
false = not restored,
|
||||||
|
failed = restoration failed due to no backup
|
||||||
|
other string = restoration successful with the string being the backup file used */
|
||||||
|
INSERT INTO "info" VALUES('gravity_restored','false');
|
||||||
|
|
||||||
CREATE TABLE domainlist_by_group
|
CREATE TABLE domainlist_by_group
|
||||||
(
|
(
|
||||||
|
158
gravity.sh
158
gravity.sh
@ -58,6 +58,8 @@ gravityDBfile_default="/etc/pihole/gravity.db"
|
|||||||
gravityTEMPfile="${GRAVITYDB}_temp"
|
gravityTEMPfile="${GRAVITYDB}_temp"
|
||||||
gravityDIR="$(dirname -- "${gravityDBfile}")"
|
gravityDIR="$(dirname -- "${gravityDBfile}")"
|
||||||
gravityOLDfile="${gravityDIR}/gravity_old.db"
|
gravityOLDfile="${gravityDIR}/gravity_old.db"
|
||||||
|
gravityBCKdir="${gravityDIR}/gravity_backups"
|
||||||
|
gravityBCKfile="${gravityBCKdir}/gravity.db"
|
||||||
|
|
||||||
fix_owner_permissions() {
|
fix_owner_permissions() {
|
||||||
# Fix ownership and permissions for the specified file
|
# Fix ownership and permissions for the specified file
|
||||||
@ -96,6 +98,15 @@ gravity_build_tree() {
|
|||||||
echo -e "${OVER} ${TICK} ${str}"
|
echo -e "${OVER} ${TICK} ${str}"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Rotate gravity backup files
|
||||||
|
rotate_gravity_backup() {
|
||||||
|
for i in {9..1}; do
|
||||||
|
if [ -f "${gravityBCKfile}.${i}" ]; then
|
||||||
|
mv "${gravityBCKfile}.${i}" "${gravityBCKfile}.$((i + 1))"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
# Copy data from old to new database file and swap them
|
# Copy data from old to new database file and swap them
|
||||||
gravity_swap_databases() {
|
gravity_swap_databases() {
|
||||||
str="Swapping databases"
|
str="Swapping databases"
|
||||||
@ -111,10 +122,32 @@ gravity_swap_databases() {
|
|||||||
oldAvail=false
|
oldAvail=false
|
||||||
if [ "${availableBlocks}" -gt "$((gravityBlocks * 2))" ] && [ -f "${gravityDBfile}" ]; then
|
if [ "${availableBlocks}" -gt "$((gravityBlocks * 2))" ] && [ -f "${gravityDBfile}" ]; then
|
||||||
oldAvail=true
|
oldAvail=true
|
||||||
mv "${gravityDBfile}" "${gravityOLDfile}"
|
cp "${gravityDBfile}" "${gravityOLDfile}"
|
||||||
else
|
|
||||||
rm "${gravityDBfile}"
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Drop the gravity and antigravity tables + subsequent VACUUM the current
|
||||||
|
# database for compaction
|
||||||
|
output=$({ printf ".timeout 30000\\nDROP TABLE IF EXISTS gravity;\\nDROP TABLE IF EXISTS antigravity;\\nVACUUM;\\n" | pihole-FTL sqlite3 -ni "${gravityDBfile}"; } 2>&1)
|
||||||
|
status="$?"
|
||||||
|
|
||||||
|
if [[ "${status}" -ne 0 ]]; then
|
||||||
|
echo -e "\\n ${CROSS} Unable to clean current database for backup\\n ${output}"
|
||||||
|
else
|
||||||
|
# Check if the backup directory exists
|
||||||
|
if [ ! -d "${gravityBCKdir}" ]; then
|
||||||
|
mkdir -p "${gravityBCKdir}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If multiple gravityBCKfile's are present (appended with a number), rotate them
|
||||||
|
# We keep at most 10 backups
|
||||||
|
rotate_gravity_backup
|
||||||
|
|
||||||
|
# Move the old database to the backup location
|
||||||
|
mv "${gravityDBfile}" "${gravityBCKfile}.1"
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
# Move the new database to the correct location
|
||||||
mv "${gravityTEMPfile}" "${gravityDBfile}"
|
mv "${gravityTEMPfile}" "${gravityDBfile}"
|
||||||
echo -e "${OVER} ${TICK} ${str}"
|
echo -e "${OVER} ${TICK} ${str}"
|
||||||
|
|
||||||
@ -324,6 +357,54 @@ gravity_CheckDNSResolutionAvailable() {
|
|||||||
echo -e "${OVER} ${TICK} DNS resolution is available"
|
echo -e "${OVER} ${TICK} DNS resolution is available"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Function: try_restore_backup
|
||||||
|
# Description: Attempts to restore the previous Pi-hole gravity database from a
|
||||||
|
# backup file. If a backup exists, it copies the backup to the
|
||||||
|
# gravity database file and prepares a new gravity database. If the
|
||||||
|
# restoration is successful, it returns 0. Otherwise, it returns 1.
|
||||||
|
# Returns:
|
||||||
|
# 0 - If the backup is successfully restored.
|
||||||
|
# 1 - If no backup is available or if the restoration fails.
|
||||||
|
try_restore_backup () {
|
||||||
|
local num filename timestamp
|
||||||
|
num=$1
|
||||||
|
filename="${gravityBCKfile}.${num}"
|
||||||
|
# Check if a backup exists
|
||||||
|
if [ -f "${filename}" ]; then
|
||||||
|
echo -e " ${INFO} Attempting to restore previous database from backup no. ${num}"
|
||||||
|
cp "${filename}" "${gravityDBfile}"
|
||||||
|
|
||||||
|
# If the backup was successfully copied, prepare a new gravity database from
|
||||||
|
# it
|
||||||
|
if [ -f "${gravityDBfile}" ]; then
|
||||||
|
output=$({ pihole-FTL sqlite3 -ni "${gravityTEMPfile}" <<<"${copyGravity}"; } 2>&1)
|
||||||
|
status="$?"
|
||||||
|
|
||||||
|
# Error checking
|
||||||
|
if [[ "${status}" -ne 0 ]]; then
|
||||||
|
echo -e "\\n ${CROSS} Unable to copy data from ${gravityDBfile} to ${gravityTEMPfile}\\n ${output}"
|
||||||
|
gravity_Cleanup "error"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get the timestamp of the backup file in a human-readable format
|
||||||
|
# Note that this timestamp will be in the server timezone, this may be
|
||||||
|
# GMT, e.g., on a Raspberry Pi where the default timezone has never been
|
||||||
|
# changed
|
||||||
|
timestamp=$(date -r "${filename}" "+%Y-%m-%d %H:%M:%S %Z")
|
||||||
|
|
||||||
|
# Add a record to the info table to indicate that the gravity database was restored
|
||||||
|
pihole-FTL sqlite3 "${gravityTEMPfile}" "INSERT OR REPLACE INTO info (property,value) values ('gravity_restored','${timestamp}');"
|
||||||
|
echo -e " ${TICK} Successfully restored from backup (${gravityBCKfile}.${num} at ${timestamp})"
|
||||||
|
return 0
|
||||||
|
else
|
||||||
|
echo -e " ${CROSS} Unable to restore backup no. ${num}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e " ${CROSS} Backup no. ${num} not available"
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
# Retrieve blocklist URLs and parse domains from adlist.list
|
# Retrieve blocklist URLs and parse domains from adlist.list
|
||||||
gravity_DownloadBlocklists() {
|
gravity_DownloadBlocklists() {
|
||||||
echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..."
|
echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..."
|
||||||
@ -332,32 +413,6 @@ gravity_DownloadBlocklists() {
|
|||||||
echo -e " ${INFO} Storing gravity database in ${COL_BOLD}${gravityDBfile}${COL_NC}"
|
echo -e " ${INFO} Storing gravity database in ${COL_BOLD}${gravityDBfile}${COL_NC}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Retrieve source URLs from gravity database
|
|
||||||
# We source only enabled adlists, SQLite3 stores boolean values as 0 (false) or 1 (true)
|
|
||||||
mapfile -t sources <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT address FROM vw_adlist;" 2>/dev/null)"
|
|
||||||
mapfile -t sourceIDs <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT id FROM vw_adlist;" 2>/dev/null)"
|
|
||||||
mapfile -t sourceTypes <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT type FROM vw_adlist;" 2>/dev/null)"
|
|
||||||
|
|
||||||
# Parse source domains from $sources
|
|
||||||
mapfile -t sourceDomains <<<"$(
|
|
||||||
# Logic: Split by folder/port
|
|
||||||
awk -F '[/:]' '{
|
|
||||||
# Remove URL protocol & optional username:password@
|
|
||||||
gsub(/(.*:\/\/|.*:.*@)/, "", $0)
|
|
||||||
if(length($1)>0){print $1}
|
|
||||||
else {print "local"}
|
|
||||||
}' <<<"$(printf '%s\n' "${sources[@]}")" 2>/dev/null
|
|
||||||
)"
|
|
||||||
|
|
||||||
local str="Pulling blocklist source list into range"
|
|
||||||
echo -e "${OVER} ${TICK} ${str}"
|
|
||||||
|
|
||||||
if [[ -z "${sources[*]}" ]] || [[ -z "${sourceDomains[*]}" ]]; then
|
|
||||||
echo -e " ${INFO} No source list found, or it is empty"
|
|
||||||
echo ""
|
|
||||||
unset sources
|
|
||||||
fi
|
|
||||||
|
|
||||||
local url domain str target compression adlist_type directory
|
local url domain str target compression adlist_type directory
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
@ -390,10 +445,53 @@ gravity_DownloadBlocklists() {
|
|||||||
|
|
||||||
if [[ "${status}" -ne 0 ]]; then
|
if [[ "${status}" -ne 0 ]]; then
|
||||||
echo -e "\\n ${CROSS} Unable to copy data from ${gravityDBfile} to ${gravityTEMPfile}\\n ${output}"
|
echo -e "\\n ${CROSS} Unable to copy data from ${gravityDBfile} to ${gravityTEMPfile}\\n ${output}"
|
||||||
return 1
|
|
||||||
|
# Try to attempt a backup restore
|
||||||
|
if [[ -d "${gravityBCKdir}" ]]; then
|
||||||
|
for i in {1..10}; do
|
||||||
|
if try_restore_backup "${i}"; then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If none of the attempts worked, return 1
|
||||||
|
if [[ "${i}" -eq 10 ]]; then
|
||||||
|
pihole-FTL sqlite3 "${gravityTEMPfile}" "INSERT OR REPLACE INTO info (property,value) values ('gravity_restored','failed');"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e " ${TICK} ${str}"
|
||||||
|
else
|
||||||
|
echo -e "${OVER} ${TICK} ${str}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Retrieve source URLs from gravity database
|
||||||
|
# We source only enabled adlists, SQLite3 stores boolean values as 0 (false) or 1 (true)
|
||||||
|
mapfile -t sources <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT address FROM vw_adlist;" 2>/dev/null)"
|
||||||
|
mapfile -t sourceIDs <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT id FROM vw_adlist;" 2>/dev/null)"
|
||||||
|
mapfile -t sourceTypes <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT type FROM vw_adlist;" 2>/dev/null)"
|
||||||
|
|
||||||
|
# Parse source domains from $sources
|
||||||
|
mapfile -t sourceDomains <<<"$(
|
||||||
|
# Logic: Split by folder/port
|
||||||
|
awk -F '[/:]' '{
|
||||||
|
# Remove URL protocol & optional username:password@
|
||||||
|
gsub(/(.*:\/\/|.*:.*@)/, "", $0)
|
||||||
|
if(length($1)>0){print $1}
|
||||||
|
else {print "local"}
|
||||||
|
}' <<<"$(printf '%s\n' "${sources[@]}")" 2>/dev/null
|
||||||
|
)"
|
||||||
|
|
||||||
|
local str="Pulling blocklist source list into range"
|
||||||
echo -e "${OVER} ${TICK} ${str}"
|
echo -e "${OVER} ${TICK} ${str}"
|
||||||
|
|
||||||
|
if [[ -z "${sources[*]}" ]] || [[ -z "${sourceDomains[*]}" ]]; then
|
||||||
|
echo -e " ${INFO} No source list found, or it is empty"
|
||||||
|
echo ""
|
||||||
|
unset sources
|
||||||
|
fi
|
||||||
|
|
||||||
# Use compression to reduce the amount of data that is transferred
|
# Use compression to reduce the amount of data that is transferred
|
||||||
# between the Pi-hole and the ad list provider. Use this feature
|
# between the Pi-hole and the ad list provider. Use this feature
|
||||||
# only if it is supported by the locally available version of curl
|
# only if it is supported by the locally available version of curl
|
||||||
|
Loading…
Reference in New Issue
Block a user