Merge development into development-v6 (with merge conflicts resolved) (#5570)

This commit is contained in:
Adam Warner 2024-02-11 17:11:04 +00:00 committed by GitHub
commit bb095bb209
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 445 additions and 414 deletions

View file

@ -23,14 +23,17 @@ jobs:
days-before-stale: 30 days-before-stale: 30
days-before-close: 5 days-before-close: 5
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.' stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.'
stale-issue-label: $stale_label stale-issue-label: '${{ env.stale_label }}'
exempt-issue-labels: 'Internal, Fixed in next release, Bug: Confirmed, Documentation Needed' exempt-issue-labels: 'Internal, Fixed in next release, Bug: Confirmed, Documentation Needed'
exempt-all-issue-assignees: true exempt-all-issue-assignees: true
operations-per-run: 300 operations-per-run: 300
close-issue-reason: 'not_planned' close-issue-reason: 'not_planned'
remove_stale: # trigger "stale" removal immediately when stale issues are commented on remove_stale:
if: github.event_name == 'issue_comment' # trigger "stale" removal immediately when stale issues are commented on
# we need to explicitly check that the trigger does not run on comment on a PR as
# https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only
if: ${{ !github.event.issue.pull_request && github.event_name != 'schedule' }}
permissions: permissions:
contents: read # for actions/checkout contents: read # for actions/checkout
issues: write # to edit issues label issues: write # to edit issues label
@ -39,7 +42,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.1.1 uses: actions/checkout@v4.1.1
- name: Remove 'stale' label - name: Remove 'stale' label
run: gh issue edit ${{ github.event.issue.number }} --remove-label $stale_label run: gh issue edit ${{ github.event.issue.number }} --remove-label ${{ env.stale_label }}
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View file

@ -27,7 +27,7 @@ colfile="/opt/pihole/COL_TABLE"
# Source api functions # Source api functions
. "${PI_HOLE_INSTALL_DIR}/api.sh" . "${PI_HOLE_INSTALL_DIR}/api.sh"
Help(){ Help() {
echo "Usage: pihole -q [option] <domain> echo "Usage: pihole -q [option] <domain>
Example: 'pihole -q --partial domain.com' Example: 'pihole -q --partial domain.com'
Query the adlists for a specified domain Query the adlists for a specified domain
@ -39,8 +39,7 @@ Options:
exit 0 exit 0
} }
GenerateOutput() {
GenerateOutput(){
local data gravity_data lists_data num_gravity num_lists search_type_str local data gravity_data lists_data num_gravity num_lists search_type_str
local gravity_data_csv lists_data_csv line current_domain local gravity_data_csv lists_data_csv line current_domain
data="${1}" data="${1}"
@ -52,8 +51,8 @@ GenerateOutput(){
gravity_data=$(printf %s "${data}" | jq '.search.gravity | group_by(.address) | map({ address: (.[0].address), domains: [.[] | .domain] })') gravity_data=$(printf %s "${data}" | jq '.search.gravity | group_by(.address) | map({ address: (.[0].address), domains: [.[] | .domain] })')
# number of objects in each json # number of objects in each json
num_gravity=$(printf %s "${gravity_data}" | jq length ) num_gravity=$(printf %s "${gravity_data}" | jq length)
num_lists=$(printf %s "${lists_data}" | jq length ) num_lists=$(printf %s "${lists_data}" | jq length)
if [ "${partial}" = true ]; then if [ "${partial}" = true ]; then
search_type_str="partially" search_type_str="partially"
@ -66,7 +65,7 @@ GenerateOutput(){
if [ "${num_lists}" -gt 0 ]; then if [ "${num_lists}" -gt 0 ]; then
# Convert the data to a csv, each line is a "domain,type" string # Convert the data to a csv, each line is a "domain,type" string
# not using jq's @csv here as it quotes each value individually # not using jq's @csv here as it quotes each value individually
lists_data_csv=$(printf %s "${lists_data}" | jq --raw-output '.[] | [.domain, .type] | join(",")' ) lists_data_csv=$(printf %s "${lists_data}" | jq --raw-output '.[] | [.domain, .type] | join(",")')
# Generate output for each csv line, separating line in a domain and type substring at the ',' # Generate output for each csv line, separating line in a domain and type substring at the ','
echo "${lists_data_csv}" | while read -r line; do echo "${lists_data_csv}" | while read -r line; do
@ -79,7 +78,7 @@ GenerateOutput(){
if [ "${num_gravity}" -gt 0 ]; then if [ "${num_gravity}" -gt 0 ]; then
# Convert the data to a csv, each line is a "URL,domain,domain,...." string # Convert the data to a csv, each line is a "URL,domain,domain,...." string
# not using jq's @csv here as it quotes each value individually # not using jq's @csv here as it quotes each value individually
gravity_data_csv=$(printf %s "${gravity_data}" | jq --raw-output '.[] | [.address, .domains[]] | join(",")' ) gravity_data_csv=$(printf %s "${gravity_data}" | jq --raw-output '.[] | [.address, .domains[]] | join(",")')
# Generate line-by-line output for each csv line # Generate line-by-line output for each csv line
echo "${gravity_data_csv}" | while read -r line; do echo "${gravity_data_csv}" | while read -r line; do
@ -103,17 +102,17 @@ GenerateOutput(){
fi fi
} }
Main(){ Main() {
local data local data
if [ -z "${domain}" ]; then if [ -z "${domain}" ]; then
echo "No domain specified"; exit 1 echo "No domain specified"
exit 1
fi fi
# domains are lowercased and converted to punycode by FTL since # domains are lowercased and converted to punycode by FTL since
# https://github.com/pi-hole/FTL/pull/1715 # https://github.com/pi-hole/FTL/pull/1715
# no need to do it here # no need to do it here
# Test if the authentication endpoint is available # Test if the authentication endpoint is available
TestAPIAvailability TestAPIAvailability
@ -138,10 +137,10 @@ Main(){
# Process all options (if present) # Process all options (if present)
while [ "$#" -gt 0 ]; do while [ "$#" -gt 0 ]; do
case "$1" in case "$1" in
"-h" | "--help" ) Help;; "-h" | "--help") Help ;;
"--partial" ) partial="true";; "--partial") partial="true" ;;
"--all" ) max_results=10000;; # hard-coded FTL limit "--all") max_results=10000 ;; # hard-coded FTL limit
* ) domain=$1;; *) domain=$1 ;;
esac esac
shift shift
done done

View file

@ -10,32 +10,31 @@
function get_local_branch() { function get_local_branch() {
# Return active branch # Return active branch
cd "${1}" 2> /dev/null || return 1 cd "${1}" 2>/dev/null || return 1
git rev-parse --abbrev-ref HEAD || return 1 git rev-parse --abbrev-ref HEAD || return 1
} }
function get_local_version() { function get_local_version() {
# Return active version # Return active version
cd "${1}" 2> /dev/null || return 1 cd "${1}" 2>/dev/null || return 1
git describe --tags --always 2> /dev/null || return 1 git describe --tags --always 2>/dev/null || return 1
} }
function get_local_hash() { function get_local_hash() {
cd "${1}" 2> /dev/null || return 1 cd "${1}" 2>/dev/null || return 1
git rev-parse --short=8 HEAD || return 1 git rev-parse --short=8 HEAD || return 1
} }
function get_remote_version() { function get_remote_version() {
# if ${2} is = "master" we need to use the "latest" endpoint, otherwise, we simply return null # if ${2} is = "master" we need to use the "latest" endpoint, otherwise, we simply return null
if [[ "${2}" == "master" ]]; then if [[ "${2}" == "master" ]]; then
curl -s "https://api.github.com/repos/pi-hole/${1}/releases/latest" 2> /dev/null | jq --raw-output .tag_name || return 1 curl -s "https://api.github.com/repos/pi-hole/${1}/releases/latest" 2>/dev/null | jq --raw-output .tag_name || return 1
else else
echo "null" echo "null"
fi fi
} }
function get_remote_hash() {
function get_remote_hash(){
git ls-remote "https://github.com/pi-hole/${1}" --tags "${2}" | awk '{print substr($0, 1,8);}' || return 1 git ls-remote "https://github.com/pi-hole/${1}" --tags "${2}" | awk '{print substr($0, 1,8);}' || return 1
} }
@ -66,7 +65,6 @@ if [[ "$1" == "reboot" ]]; then
sleep 30 sleep 30
fi fi
# get Core versions # get Core versions
CORE_VERSION="$(get_local_version /etc/.pihole)" CORE_VERSION="$(get_local_version /etc/.pihole)"
@ -84,7 +82,6 @@ addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_CORE_VERSION" "${GITHUB_CORE_VERSI
GITHUB_CORE_HASH="$(get_remote_hash pi-hole "${CORE_BRANCH}")" GITHUB_CORE_HASH="$(get_remote_hash pi-hole "${CORE_BRANCH}")"
addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_CORE_HASH" "${GITHUB_CORE_HASH}" addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_CORE_HASH" "${GITHUB_CORE_HASH}"
# get Web versions # get Web versions
WEB_VERSION="$(get_local_version /var/www/html/admin)" WEB_VERSION="$(get_local_version /var/www/html/admin)"
@ -119,7 +116,6 @@ addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_FTL_VERSION" "${GITHUB_FTL_VERSION
GITHUB_FTL_HASH="$(get_remote_hash FTL "${FTL_BRANCH}")" GITHUB_FTL_HASH="$(get_remote_hash FTL "${FTL_BRANCH}")"
addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_FTL_HASH" "${GITHUB_FTL_HASH}" addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_FTL_HASH" "${GITHUB_FTL_HASH}"
# get Docker versions # get Docker versions
if [[ "${DOCKER_TAG}" ]]; then if [[ "${DOCKER_TAG}" ]]; then

View file

@ -31,7 +31,7 @@ main() {
# Automatically show detailed information if # Automatically show detailed information if
# at least one of the components is not on master branch # at least one of the components is not on master branch
if [ ! "${CORE_BRANCH}" = "master" ] || [ ! "${WEB_BRANCH}" = "master" ] || [ ! "${FTL_BRANCH}" = "master" ] ; then if [ ! "${CORE_BRANCH}" = "master" ] || [ ! "${WEB_BRANCH}" = "master" ] || [ ! "${FTL_BRANCH}" = "master" ]; then
details=true details=true
fi fi

View file

@ -39,9 +39,9 @@ export PATH+=':/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
: "${DIALOG_CANCEL:=1}" : "${DIALOG_CANCEL:=1}"
: "${DIALOG_ESC:=255}" : "${DIALOG_ESC:=255}"
# List of supported DNS servers # List of supported DNS servers
DNS_SERVERS=$(cat << EOM DNS_SERVERS=$(
cat <<EOM
Google (ECS, DNSSEC);8.8.8.8;8.8.4.4;2001:4860:4860:0:0:0:0:8888;2001:4860:4860:0:0:0:0:8844 Google (ECS, DNSSEC);8.8.8.8;8.8.4.4;2001:4860:4860:0:0:0:0:8888;2001:4860:4860:0:0:0:0:8844
OpenDNS (ECS, DNSSEC);208.67.222.222;208.67.220.220;2620:119:35::35;2620:119:53::53 OpenDNS (ECS, DNSSEC);208.67.222.222;208.67.220.220;2620:119:35::35;2620:119:53::53
Level3;4.2.2.1;4.2.2.2;; Level3;4.2.2.1;4.2.2.2;;
@ -62,7 +62,6 @@ coltable="/opt/pihole/COL_TABLE"
# Root of the web server # Root of the web server
webroot="/var/www/html" webroot="/var/www/html"
# We clone (or update) two git repositories during the install. This helps to make sure that we always have the latest versions of the relevant files. # We clone (or update) two git repositories during the install. This helps to make sure that we always have the latest versions of the relevant files.
# web is used to set up the Web admin interface. # web is used to set up the Web admin interface.
# Pi-hole contains various setup scripts and files which are critical to the installation. # Pi-hole contains various setup scripts and files which are critical to the installation.
@ -107,8 +106,8 @@ runUnattended=false
# Check arguments for the undocumented flags # Check arguments for the undocumented flags
for var in "$@"; do for var in "$@"; do
case "$var" in case "$var" in
"--reconfigure" ) reconfigure=true;; "--reconfigure") reconfigure=true ;;
"--unattended" ) runUnattended=true;; "--unattended") runUnattended=true ;;
esac esac
done done
@ -176,7 +175,10 @@ os_check() {
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"') detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
# Test via IPv4 # Test via IPv4
cmdResult="$(dig -4 +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)" cmdResult="$(
dig -4 +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1
echo $?
)"
# Gets the return code of the previous command (last line) # Gets the return code of the previous command (last line)
digReturnCode="${cmdResult##*$'\n'}" digReturnCode="${cmdResult##*$'\n'}"
@ -197,7 +199,10 @@ os_check() {
if [ "$valid_response" = false ]; then if [ "$valid_response" = false ]; then
unset valid_response unset valid_response
cmdResult="$(dig -6 +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)" cmdResult="$(
dig -6 +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1
echo $?
)"
# Gets the return code of the previous command (last line) # Gets the return code of the previous command (last line)
digReturnCode="${cmdResult##*$'\n'}" digReturnCode="${cmdResult##*$'\n'}"
@ -217,8 +222,7 @@ os_check() {
if [ "$valid_response" = true ]; then if [ "$valid_response" = true ]; then
IFS=" " read -r -a supportedOS < <(echo "${response}" | tr -d '"') IFS=" " read -r -a supportedOS < <(echo "${response}" | tr -d '"')
for distro_and_versions in "${supportedOS[@]}" for distro_and_versions in "${supportedOS[@]}"; do
do
distro_part="${distro_and_versions%%=*}" distro_part="${distro_and_versions%%=*}"
versions_part="${distro_and_versions##*=}" versions_part="${distro_and_versions##*=}"
@ -226,8 +230,7 @@ os_check() {
if [[ "${detected_os^^}" =~ ${distro_part^^} ]]; then if [[ "${detected_os^^}" =~ ${distro_part^^} ]]; then
valid_os=true valid_os=true
IFS="," read -r -a supportedVer <<<"${versions_part}" IFS="," read -r -a supportedVer <<<"${versions_part}"
for version in "${supportedVer[@]}" for version in "${supportedVer[@]}"; do
do
if [[ "${detected_version}" =~ $version ]]; then if [[ "${detected_version}" =~ $version ]]; then
valid_version=true valid_version=true
break break
@ -292,17 +295,16 @@ test_dpkg_lock() {
printf " %b Waiting for package manager to finish (up to 30 seconds)\\n" "${INFO}" printf " %b Waiting for package manager to finish (up to 30 seconds)\\n" "${INFO}"
# fuser is a program to show which processes use the named files, sockets, or filesystems # fuser is a program to show which processes use the named files, sockets, or filesystems
# So while the lock is held, # So while the lock is held,
while fuser /var/lib/dpkg/lock >/dev/null 2>&1 while fuser /var/lib/dpkg/lock >/dev/null 2>&1; do
do
# we wait half a second, # we wait half a second,
sleep 0.5 sleep 0.5
# increase the iterator, # increase the iterator,
((i=i+1)) ((i = i + 1))
# exit if waiting for more then 30 seconds # exit if waiting for more then 30 seconds
if [[ $i -gt 60 ]]; then if [[ $i -gt 60 ]]; then
printf " %b %bError: Could not verify package manager finished and released lock. %b\\n" "${CROSS}" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b %bError: Could not verify package manager finished and released lock. %b\\n" "${CROSS}" "${COL_LIGHT_RED}" "${COL_NC}"
printf " Attempt to install packages manually and retry.\\n" printf " Attempt to install packages manually and retry.\\n"
exit 1; exit 1
fi fi
done done
# and then report success once dpkg is unlocked. # and then report success once dpkg is unlocked.
@ -315,7 +317,7 @@ package_manager_detect() {
# the distro-specific ones below. # the distro-specific ones below.
# First check to see if apt-get is installed. # First check to see if apt-get is installed.
if is_command apt-get ; then if is_command apt-get; then
# Set some global variables here # Set some global variables here
# We don't set them earlier since the installed package manager might be rpm, so these values would be different # We don't set them earlier since the installed package manager might be rpm, so these values would be different
PKG_MANAGER="apt-get" PKG_MANAGER="apt-get"
@ -335,9 +337,9 @@ package_manager_detect() {
PIHOLE_DEPS=(cron curl iputils-ping psmisc sudo unzip libcap2-bin dns-root-data libcap2 netcat-openbsd procps jq lshw bash-completion) PIHOLE_DEPS=(cron curl iputils-ping psmisc sudo unzip libcap2-bin dns-root-data libcap2 netcat-openbsd procps jq lshw bash-completion)
# If apt-get is not found, check for rpm. # If apt-get is not found, check for rpm.
elif is_command rpm ; then elif is_command rpm; then
# Then check if dnf or yum is the package manager # Then check if dnf or yum is the package manager
if is_command dnf ; then if is_command dnf; then
PKG_MANAGER="dnf" PKG_MANAGER="dnf"
else else
PKG_MANAGER="yum" PKG_MANAGER="yum"
@ -370,17 +372,17 @@ is_repo() {
# If the first argument passed to this function is a directory, # If the first argument passed to this function is a directory,
if [[ -d "${directory}" ]]; then if [[ -d "${directory}" ]]; then
# move into the directory # move into the directory
pushd "${directory}" &> /dev/null || return 1 pushd "${directory}" &>/dev/null || return 1
# Use git to check if the directory is a repo # Use git to check if the directory is a repo
# git -C is not used here to support git versions older than 1.8.4 # git -C is not used here to support git versions older than 1.8.4
git status --short &> /dev/null || rc=$? git status --short &>/dev/null || rc=$?
# If the command was not successful, # If the command was not successful,
else else
# Set a non-zero return code if directory does not exist # Set a non-zero return code if directory does not exist
rc=1 rc=1
fi fi
# Move back into the directory the user started in # Move back into the directory the user started in
popd &> /dev/null || return 1 popd &>/dev/null || return 1
# Return the code; if one is not set, return 0 # Return the code; if one is not set, return 0
return "${rc:-0}" return "${rc:-0}"
} }
@ -403,9 +405,9 @@ make_repo() {
return 1 return 1
fi fi
# Clone the repo and return the return code from this command # Clone the repo and return the return code from this command
git clone -q --depth 20 "${remoteRepo}" "${directory}" &> /dev/null || return $? git clone -q --depth 20 "${remoteRepo}" "${directory}" &>/dev/null || return $?
# Move into the directory that was passed as an argument # Move into the directory that was passed as an argument
pushd "${directory}" &> /dev/null || return 1 pushd "${directory}" &>/dev/null || return 1
# Check current branch. If it is master, then reset to the latest available tag. # Check current branch. If it is master, then reset to the latest available tag.
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD) curBranch=$(git rev-parse --abbrev-ref HEAD)
@ -418,7 +420,7 @@ make_repo() {
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git) # Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}" chmod -R a+rX "${directory}"
# Move back into the original directory # Move back into the original directory
popd &> /dev/null || return 1 popd &>/dev/null || return 1
return 0 return 0
} }
@ -436,14 +438,14 @@ update_repo() {
# we only need to make one change here # we only need to make one change here
local str="Update repo in ${1}" local str="Update repo in ${1}"
# Move into the directory that was passed as an argument # Move into the directory that was passed as an argument
pushd "${directory}" &> /dev/null || return 1 pushd "${directory}" &>/dev/null || return 1
# Let the user know what's happening # Let the user know what's happening
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# Stash any local commits as they conflict with our working code # Stash any local commits as they conflict with our working code
git stash --all --quiet &> /dev/null || true # Okay for stash failure git stash --all --quiet &>/dev/null || true # Okay for stash failure
git clean --quiet --force -d || true # Okay for already clean directory git clean --quiet --force -d || true # Okay for already clean directory
# Pull the latest commits # Pull the latest commits
git pull --no-rebase --quiet &> /dev/null || return $? git pull --no-rebase --quiet &>/dev/null || return $?
# Check current branch. If it is master, then reset to the latest available tag. # Check current branch. If it is master, then reset to the latest available tag.
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD) curBranch=$(git rev-parse --abbrev-ref HEAD)
@ -455,7 +457,7 @@ update_repo() {
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git) # Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}" chmod -R a+rX "${directory}"
# Move back into the original directory # Move back into the original directory
popd &> /dev/null || return 1 popd &>/dev/null || return 1
return 0 return 0
} }
@ -475,13 +477,19 @@ getGitFiles() {
# Show that we're checking it # Show that we're checking it
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Update the repo, returning an error message on failure # Update the repo, returning an error message on failure
update_repo "${directory}" || { printf "\\n %b: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } update_repo "${directory}" || {
printf "\\n %b: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
exit 1
}
# If it's not a .git repo, # If it's not a .git repo,
else else
# Show an error # Show an error
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
# Attempt to make the repository, showing an error on failure # Attempt to make the repository, showing an error on failure
make_repo "${directory}" "${remoteRepo}" || { printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } make_repo "${directory}" "${remoteRepo}" || {
printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
exit 1
}
fi fi
echo "" echo ""
# Success via one of the two branches, as the commands would exit if they failed. # Success via one of the two branches, as the commands would exit if they failed.
@ -493,19 +501,19 @@ resetRepo() {
# Use named variables for arguments # Use named variables for arguments
local directory="${1}" local directory="${1}"
# Move into the directory # Move into the directory
pushd "${directory}" &> /dev/null || return 1 pushd "${directory}" &>/dev/null || return 1
# Store the message in a variable # Store the message in a variable
str="Resetting repository within ${1}..." str="Resetting repository within ${1}..."
# Show the message # Show the message
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# Use git to remove the local changes # Use git to remove the local changes
git reset --hard &> /dev/null || return $? git reset --hard &>/dev/null || return $?
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git) # Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}" chmod -R a+rX "${directory}"
# And show the status # And show the status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Return to where we came from # Return to where we came from
popd &> /dev/null || return 1 popd &>/dev/null || return 1
# Function succeeded, as "git reset" would have triggered a return earlier if it failed # Function succeeded, as "git reset" would have triggered a return earlier if it failed
return 0 return 0
} }
@ -527,7 +535,7 @@ find_IPv4_information() {
# the variable with just the first field. # the variable with just the first field.
printf -v IPv4bare "$(printf ${route#*src })" printf -v IPv4bare "$(printf ${route#*src })"
if ! valid_ip "${IPv4bare}" ; then if ! valid_ip "${IPv4bare}"; then
IPv4bare="127.0.0.1" IPv4bare="127.0.0.1"
fi fi
@ -563,7 +571,7 @@ welcomeDialogs() {
--yesno "\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly.\\n\\n\ --yesno "\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly.\\n\\n\
\\Zb\\Z1IMPORTANT:\\Zn If you have not already done so, you must ensure that this device has a static IP.\\n\\n\ \\Zb\\Z1IMPORTANT:\\Zn If you have not already done so, you must ensure that this device has a static IP.\\n\\n\
Depending on your operating system, there are many ways to achieve this, through DHCP reservation, or by manually assigning one.\\n\\n\ Depending on your operating system, there are many ways to achieve this, through DHCP reservation, or by manually assigning one.\\n\\n\
Please continue when the static addressing has been configured."\ Please continue when the static addressing has been configured." \
"${r}" "${c}" && result=0 || result="$?" "${r}" "${c}" && result=0 || result="$?"
case "${result}" in case "${result}" in
@ -609,7 +617,7 @@ chooseInterface() {
result=$? result=$?
case ${result} in case ${result} in
"${DIALOG_CANCEL}"|"${DIALOG_ESC}") "${DIALOG_CANCEL}" | "${DIALOG_ESC}")
# Show an error message and exit # Show an error message and exit
printf " %b %s\\n" "${CROSS}" "No interface selected, exiting installer" printf " %b %s\\n" "${CROSS}" "No interface selected, exiting installer"
exit 1 exit 1
@ -627,21 +635,21 @@ testIPv6() {
# first will contain fda2 (ULA) # first will contain fda2 (ULA)
printf -v first "%s" "${1%%:*}" printf -v first "%s" "${1%%:*}"
# value1 will contain 253 which is the decimal value corresponding to 0xFD # value1 will contain 253 which is the decimal value corresponding to 0xFD
value1=$(( (0x$first)/256 )) value1=$(((0x$first) / 256))
# value2 will contain 162 which is the decimal value corresponding to 0xA2 # value2 will contain 162 which is the decimal value corresponding to 0xA2
value2=$(( (0x$first)%256 )) value2=$(((0x$first) % 256))
# the ULA test is testing for fc00::/7 according to RFC 4193 # the ULA test is testing for fc00::/7 according to RFC 4193
if (( (value1&254)==252 )); then if (((value1 & 254) == 252)); then
# echoing result to calling function as return value # echoing result to calling function as return value
echo "ULA" echo "ULA"
fi fi
# the GUA test is testing for 2000::/3 according to RFC 4291 # the GUA test is testing for 2000::/3 according to RFC 4291
if (( (value1&112)==32 )); then if (((value1 & 112) == 32)); then
# echoing result to calling function as return value # echoing result to calling function as return value
echo "GUA" echo "GUA"
fi fi
# the LL test is testing for fe80::/10 according to RFC 4193 # the LL test is testing for fe80::/10 according to RFC 4193
if (( (value1)==254 )) && (( (value2&192)==128 )); then if (((value1) == 254)) && (((value2 & 192) == 128)); then
# echoing result to calling function as return value # echoing result to calling function as return value
echo "Link-local" echo "Link-local"
fi fi
@ -699,9 +707,9 @@ valid_ip() {
# Regex matching one IPv4 component, i.e. an integer from 0 to 255. # Regex matching one IPv4 component, i.e. an integer from 0 to 255.
# See https://tools.ietf.org/html/rfc1340 # See https://tools.ietf.org/html/rfc1340
local ipv4elem="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"; local ipv4elem="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)"
# Regex matching an optional port (starting with '#') range of 1-65536 # Regex matching an optional port (starting with '#') range of 1-65536
local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?"; local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?"
# Build a full IPv4 regex from the above subexpressions # Build a full IPv4 regex from the above subexpressions
local regex="^${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}${portelem}$" local regex="^${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}${portelem}$"
@ -721,7 +729,7 @@ valid_ip6() {
# Regex matching an IPv6 CIDR, i.e. 1 to 128 # Regex matching an IPv6 CIDR, i.e. 1 to 128
local v6cidr="(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}" local v6cidr="(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}"
# Regex matching an optional port (starting with '#') range of 1-65536 # Regex matching an optional port (starting with '#') range of 1-65536
local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?"; local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?"
# Build a full IPv6 regex from the above subexpressions # Build a full IPv6 regex from the above subexpressions
local regex="^(((${ipv6elem}))*((:${ipv6elem}))*::((${ipv6elem}))*((:${ipv6elem}))*|((${ipv6elem}))((:${ipv6elem})){7})${v6cidr}${portelem}$" local regex="^(((${ipv6elem}))*((:${ipv6elem}))*::((${ipv6elem}))*((:${ipv6elem}))*|((${ipv6elem}))((:${ipv6elem})){7})${v6cidr}${portelem}$"
@ -745,16 +753,15 @@ setDNS() {
# and set the new one to newline # and set the new one to newline
IFS=$'\n' IFS=$'\n'
# Put the DNS Servers into an array # Put the DNS Servers into an array
for DNSServer in ${DNS_SERVERS} for DNSServer in ${DNS_SERVERS}; do
do DNSName="$(cut -d';' -f1 <<<"${DNSServer}")"
DNSName="$(cut -d';' -f1 <<< "${DNSServer}")"
DNSChooseOptions[DNSServerCount]="${DNSName}" DNSChooseOptions[DNSServerCount]="${DNSName}"
(( DNSServerCount=DNSServerCount+1 )) ((DNSServerCount = DNSServerCount + 1))
DNSChooseOptions[DNSServerCount]="" DNSChooseOptions[DNSServerCount]=""
(( DNSServerCount=DNSServerCount+1 )) ((DNSServerCount = DNSServerCount + 1))
done done
DNSChooseOptions[DNSServerCount]="Custom" DNSChooseOptions[DNSServerCount]="Custom"
(( DNSServerCount=DNSServerCount+1 )) ((DNSServerCount = DNSServerCount + 1))
DNSChooseOptions[DNSServerCount]="" DNSChooseOptions[DNSServerCount]=""
# Restore the IFS to what it was # Restore the IFS to what it was
IFS=${OIFS} IFS=${OIFS}
@ -773,8 +780,7 @@ setDNS() {
esac esac
# Depending on the user's choice, set the GLOBAL variables to the IP of the respective provider # Depending on the user's choice, set the GLOBAL variables to the IP of the respective provider
if [[ "${DNSchoices}" == "Custom" ]] if [[ "${DNSchoices}" == "Custom" ]]; then
then
# Loop until we have a valid DNS setting # Loop until we have a valid DNS setting
until [[ "${DNSSettingsCorrect}" = True ]]; do until [[ "${DNSSettingsCorrect}" = True ]]; do
# Signal value, to be used if the user inputs an invalid IP address # Signal value, to be used if the user inputs an invalid IP address
@ -799,7 +805,7 @@ setDNS() {
--backtitle "Specify Upstream DNS Provider(s)" \ --backtitle "Specify Upstream DNS Provider(s)" \
--inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\ --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\
If you want to specify a port other than 53, separate it with a hash.\ If you want to specify a port other than 53, separate it with a hash.\
\\n\\nFor example '8.8.8.8, 8.8.4.4' or '127.0.0.1#5335'"\ \\n\\nFor example '8.8.8.8, 8.8.4.4' or '127.0.0.1#5335'" \
"${r}" "${c}" "${prePopulate}") "${r}" "${c}" "${prePopulate}")
result=$? result=$?
@ -811,7 +817,7 @@ If you want to specify a port other than 53, separate it with a hash.\
esac esac
# Clean user input and replace whitespace with comma. # Clean user input and replace whitespace with comma.
piholeDNS=$(sed 's/[, \t]\+/,/g' <<< "${piholeDNS}") piholeDNS=$(sed 's/[, \t]\+/,/g' <<<"${piholeDNS}")
# Separate the user input into the two DNS values (separated by a comma) # Separate the user input into the two DNS values (separated by a comma)
printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}" printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}"
@ -870,13 +876,11 @@ If you want to specify a port other than 53, separate it with a hash.\
OIFS=$IFS OIFS=$IFS
# and set the new one to newline # and set the new one to newline
IFS=$'\n' IFS=$'\n'
for DNSServer in ${DNS_SERVERS} for DNSServer in ${DNS_SERVERS}; do
do DNSName="$(cut -d';' -f1 <<<"${DNSServer}")"
DNSName="$(cut -d';' -f1 <<< "${DNSServer}")" if [[ "${DNSchoices}" == "${DNSName}" ]]; then
if [[ "${DNSchoices}" == "${DNSName}" ]] PIHOLE_DNS_1="$(cut -d';' -f2 <<<"${DNSServer}")"
then PIHOLE_DNS_2="$(cut -d';' -f3 <<<"${DNSServer}")"
PIHOLE_DNS_1="$(cut -d';' -f2 <<< "${DNSServer}")"
PIHOLE_DNS_2="$(cut -d';' -f3 <<< "${DNSServer}")"
break break
fi fi
done done
@ -963,7 +967,7 @@ chooseBlocklists() {
"${DIALOG_OK}") "${DIALOG_OK}")
# If they chose yes, # If they chose yes,
printf " %b Installing StevenBlack's Unified Hosts List\\n" "${INFO}" printf " %b Installing StevenBlack's Unified Hosts List\\n" "${INFO}"
echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}" echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >>"${adlistFile}"
;; ;;
"${DIALOG_CANCEL}") "${DIALOG_CANCEL}")
# If they chose no, # If they chose no,
@ -989,9 +993,9 @@ installDefaultBlocklists() {
# In unattended setup, could be useful to use userdefined blocklist. # In unattended setup, could be useful to use userdefined blocklist.
# If this file exists, we avoid overriding it. # If this file exists, we avoid overriding it.
if [[ -f "${adlistFile}" ]]; then if [[ -f "${adlistFile}" ]]; then
return; return
fi fi
echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}" echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >>"${adlistFile}"
} }
remove_old_dnsmasq_ftl_configs() { remove_old_dnsmasq_ftl_configs() {
@ -1033,7 +1037,6 @@ remove_old_pihole_lighttpd_configs() {
local confavailable="/etc/lighttpd/conf-available/15-pihole-admin.conf" local confavailable="/etc/lighttpd/conf-available/15-pihole-admin.conf"
local confenabled="/etc/lighttpd/conf-enabled/15-pihole-admin.conf" local confenabled="/etc/lighttpd/conf-enabled/15-pihole-admin.conf"
if [[ -f "${lighttpdConfig}" ]]; then if [[ -f "${lighttpdConfig}" ]]; then
sed -i '/include "\/etc\/lighttpd\/conf.d\/pihole-admin.conf"/d' "${lighttpdConfig}" sed -i '/include "\/etc\/lighttpd\/conf.d\/pihole-admin.conf"/d' "${lighttpdConfig}"
fi fi
@ -1042,8 +1045,8 @@ remove_old_pihole_lighttpd_configs() {
rm "${condfd}" rm "${condfd}"
fi fi
if is_command lighty-disable-mod ; then if is_command lighty-disable-mod; then
lighty-disable-mod pihole-admin > /dev/null || true lighty-disable-mod pihole-admin >/dev/null || true
fi fi
if [[ -f "${confavailable}" ]]; then if [[ -f "${confavailable}" ]]; then
@ -1063,7 +1066,7 @@ clean_existing() {
# Pop the first argument, and shift all addresses down by one (i.e. ${2} becomes ${1}) # Pop the first argument, and shift all addresses down by one (i.e. ${2} becomes ${1})
shift shift
# Then, we can access all arguments ($@) without including the directory to clean # Then, we can access all arguments ($@) without including the directory to clean
local old_files=( "$@" ) local old_files=("$@")
# Remove each script in the old_files array # Remove each script in the old_files array
for script in "${old_files[@]}"; do for script in "${old_files[@]}"; do
@ -1117,7 +1120,7 @@ installConfigs() {
# Install list of DNS servers # Install list of DNS servers
# Format: Name;Primary IPv4;Secondary IPv4;Primary IPv6;Secondary IPv6 # Format: Name;Primary IPv4;Secondary IPv4;Primary IPv6;Secondary IPv6
# Some values may be empty (for example: DNS servers without IPv6 support) # Some values may be empty (for example: DNS servers without IPv6 support)
echo "${DNS_SERVERS}" > "${PI_HOLE_CONFIG_DIR}/dns-servers.conf" echo "${DNS_SERVERS}" >"${PI_HOLE_CONFIG_DIR}/dns-servers.conf"
chmod 644 "${PI_HOLE_CONFIG_DIR}/dns-servers.conf" chmod 644 "${PI_HOLE_CONFIG_DIR}/dns-servers.conf"
chown pihole:pihole "${PI_HOLE_CONFIG_DIR}/dns-servers.conf" chown pihole:pihole "${PI_HOLE_CONFIG_DIR}/dns-servers.conf"
@ -1154,7 +1157,7 @@ install_manpage() {
# Default location for man files for /usr/local/bin is /usr/local/share/man # Default location for man files for /usr/local/bin is /usr/local/share/man
# on lightweight systems may not be present, so check before copying. # on lightweight systems may not be present, so check before copying.
printf " %b Testing man page installation" "${INFO}" printf " %b Testing man page installation" "${INFO}"
if ! is_command mandb ; then if ! is_command mandb; then
# if mandb is not present, no manpage support # if mandb is not present, no manpage support
printf "%b %b man not installed\\n" "${OVER}" "${INFO}" printf "%b %b man not installed\\n" "${OVER}" "${INFO}"
return return
@ -1199,10 +1202,10 @@ stop_service() {
# Can softfail, as process may not be installed when this is called # Can softfail, as process may not be installed when this is called
local str="Stopping ${1} service" local str="Stopping ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
if is_command systemctl ; then if is_command systemctl; then
systemctl stop "${1}" &> /dev/null || true systemctl stop "${1}" &>/dev/null || true
else else
service "${1}" stop &> /dev/null || true service "${1}" stop &>/dev/null || true
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1213,12 +1216,12 @@ restart_service() {
local str="Restarting ${1} service" local str="Restarting ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemctl exists, # If systemctl exists,
if is_command systemctl ; then if is_command systemctl; then
# use that to restart the service # use that to restart the service
systemctl restart "${1}" &> /dev/null systemctl restart "${1}" &>/dev/null
else else
# Otherwise, fall back to the service command # Otherwise, fall back to the service command
service "${1}" restart &> /dev/null service "${1}" restart &>/dev/null
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1229,12 +1232,12 @@ enable_service() {
local str="Enabling ${1} service to start on reboot" local str="Enabling ${1} service to start on reboot"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemctl exists, # If systemctl exists,
if is_command systemctl ; then if is_command systemctl; then
# use that to enable the service # use that to enable the service
systemctl enable "${1}" &> /dev/null systemctl enable "${1}" &>/dev/null
else else
# Otherwise, use update-rc.d to accomplish this # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" defaults &> /dev/null update-rc.d "${1}" defaults &>/dev/null
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
@ -1245,24 +1248,24 @@ disable_service() {
local str="Disabling ${1} service" local str="Disabling ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemctl exists, # If systemctl exists,
if is_command systemctl ; then if is_command systemctl; then
# use that to disable the service # use that to disable the service
systemctl disable "${1}" &> /dev/null systemctl disable "${1}" &>/dev/null
else else
# Otherwise, use update-rc.d to accomplish this # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" disable &> /dev/null update-rc.d "${1}" disable &>/dev/null
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
} }
check_service_active() { check_service_active() {
# If systemctl exists, # If systemctl exists,
if is_command systemctl ; then if is_command systemctl; then
# use that to check the status of the service # use that to check the status of the service
systemctl is-enabled "${1}" &> /dev/null systemctl is-enabled "${1}" &>/dev/null
else else
# Otherwise, fall back to service command # Otherwise, fall back to service command
service "${1}" status &> /dev/null service "${1}" status &>/dev/null
fi fi
} }
@ -1273,7 +1276,7 @@ disable_resolved_stublistener() {
if check_service_active "systemd-resolved"; then if check_service_active "systemd-resolved"; then
# Check if DNSStubListener is enabled # Check if DNSStubListener is enabled
printf " %b %b Testing if systemd-resolved DNSStub-Listener is active" "${OVER}" "${INFO}" printf " %b %b Testing if systemd-resolved DNSStub-Listener is active" "${OVER}" "${INFO}"
if ( grep -E '#?DNSStubListener=yes' /etc/systemd/resolved.conf &> /dev/null ); then if (grep -E '#?DNSStubListener=yes' /etc/systemd/resolved.conf &>/dev/null); then
# Disable the DNSStubListener to unbind it from port 53 # Disable the DNSStubListener to unbind it from port 53
# Note that this breaks dns functionality on host until ftl are up and running # Note that this breaks dns functionality on host until ftl are up and running
printf "%b %b Disabling systemd-resolved DNSStubListener" "${OVER}" "${TICK}" printf "%b %b Disabling systemd-resolved DNSStubListener" "${OVER}" "${TICK}"
@ -1298,14 +1301,14 @@ update_package_cache() {
local str="Update local cache of available packages" local str="Update local cache of available packages"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# Create a command from the package cache variable # Create a command from the package cache variable
if eval "${UPDATE_PKG_CACHE}" &> /dev/null; then if eval "${UPDATE_PKG_CACHE}" &>/dev/null; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
# Otherwise, show an error and exit # Otherwise, show an error and exit
# In case we used apt-get and apt is also available, we use this as recommendation as we have seen it # In case we used apt-get and apt is also available, we use this as recommendation as we have seen it
# gives more user-friendly (interactive) advice # gives more user-friendly (interactive) advice
if [[ ${PKG_MANAGER} == "apt-get" ]] && is_command apt ; then if [[ ${PKG_MANAGER} == "apt-get" ]] && is_command apt; then
UPDATE_PKG_CACHE="apt update" UPDATE_PKG_CACHE="apt update"
fi fi
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
@ -1347,11 +1350,11 @@ install_dependent_packages() {
# amount of download traffic. # amount of download traffic.
# NOTE: We may be able to use this installArray in the future to create a list of package that were # NOTE: We may be able to use this installArray in the future to create a list of package that were
# installed by us, and remove only the installed packages, and not the entire list. # installed by us, and remove only the installed packages, and not the entire list.
if is_command apt-get ; then if is_command apt-get; then
# For each package, check if it's already installed (and if so, don't add it to the installArray) # For each package, check if it's already installed (and if so, don't add it to the installArray)
for i in "$@"; do for i in "$@"; do
printf " %b Checking for %s..." "${INFO}" "${i}" printf " %b Checking for %s..." "${INFO}" "${i}"
if dpkg-query -W -f='${Status}' "${i}" 2>/dev/null | grep "ok installed" &> /dev/null; then if dpkg-query -W -f='${Status}' "${i}" 2>/dev/null | grep "ok installed" &>/dev/null; then
printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}"
else else
printf "%b %b Checking for %s (will be installed)\\n" "${OVER}" "${INFO}" "${i}" printf "%b %b Checking for %s (will be installed)\\n" "${OVER}" "${INFO}" "${i}"
@ -1364,9 +1367,9 @@ install_dependent_packages() {
# Running apt-get install with minimal output can cause some issues with # Running apt-get install with minimal output can cause some issues with
# requiring user input (e.g password for phpmyadmin see #218) # requiring user input (e.g password for phpmyadmin see #218)
printf " %b Processing %s install(s) for: %s, please wait...\\n" "${INFO}" "${PKG_MANAGER}" "${installArray[*]}" printf " %b Processing %s install(s) for: %s, please wait...\\n" "${INFO}" "${PKG_MANAGER}" "${installArray[*]}"
printf '%*s\n' "${c}" '' | tr " " -; printf '%*s\n' "${c}" '' | tr " " -
"${PKG_INSTALL[@]}" "${installArray[@]}" "${PKG_INSTALL[@]}" "${installArray[@]}"
printf '%*s\n' "${c}" '' | tr " " -; printf '%*s\n' "${c}" '' | tr " " -
return return
fi fi
printf "\\n" printf "\\n"
@ -1377,7 +1380,7 @@ install_dependent_packages() {
for i in "$@"; do for i in "$@"; do
# For each package, check if it's already installed (and if so, don't add it to the installArray) # For each package, check if it's already installed (and if so, don't add it to the installArray)
printf " %b Checking for %s..." "${INFO}" "${i}" printf " %b Checking for %s..." "${INFO}" "${i}"
if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then if "${PKG_MANAGER}" -q list installed "${i}" &>/dev/null; then
printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}"
else else
printf "%b %b Checking for %s (will be installed)\\n" "${OVER}" "${INFO}" "${i}" printf "%b %b Checking for %s (will be installed)\\n" "${OVER}" "${INFO}" "${i}"
@ -1387,9 +1390,9 @@ install_dependent_packages() {
# If there's anything to install, install everything in the list. # If there's anything to install, install everything in the list.
if [[ "${#installArray[@]}" -gt 0 ]]; then if [[ "${#installArray[@]}" -gt 0 ]]; then
printf " %b Processing %s install(s) for: %s, please wait...\\n" "${INFO}" "${PKG_MANAGER}" "${installArray[*]}" printf " %b Processing %s install(s) for: %s, please wait...\\n" "${INFO}" "${PKG_MANAGER}" "${installArray[*]}"
printf '%*s\n' "${c}" '' | tr " " -; printf '%*s\n' "${c}" '' | tr " " -
"${PKG_INSTALL[@]}" "${installArray[@]}" "${PKG_INSTALL[@]}" "${installArray[@]}"
printf '%*s\n' "${c}" '' | tr " " -; printf '%*s\n' "${c}" '' | tr " " -
return return
fi fi
printf "\\n" printf "\\n"
@ -1423,9 +1426,9 @@ create_pihole_user() {
local str="Checking for user 'pihole'" local str="Checking for user 'pihole'"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If the pihole user exists, # If the pihole user exists,
if id -u pihole &> /dev/null; then if id -u pihole &>/dev/null; then
# and if the pihole group exists, # and if the pihole group exists,
if getent group pihole > /dev/null 2>&1; then if getent group pihole >/dev/null 2>&1; then
# succeed # succeed
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
@ -1452,7 +1455,7 @@ create_pihole_user() {
printf "%b %b %s" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s" "${OVER}" "${CROSS}" "${str}"
local str="Checking for group 'pihole'" local str="Checking for group 'pihole'"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
if getent group pihole > /dev/null 2>&1; then if getent group pihole >/dev/null 2>&1; then
# group pihole exists # group pihole exists
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# then create and add her to the pihole group # then create and add her to the pihole group
@ -1592,7 +1595,7 @@ checkSelinux() {
;; ;;
esac esac
else else
echo -e " ${INFO} ${COL_GREEN}SELinux not detected${COL_NC}"; echo -e " ${INFO} ${COL_GREEN}SELinux not detected${COL_NC}"
fi fi
# Exit the installer if any SELinux checks toggled the flag # Exit the installer if any SELinux checks toggled the flag
if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then
@ -1601,8 +1604,8 @@ checkSelinux() {
printf " This check can be skipped by setting the environment variable %bPIHOLE_SELINUX%b to %btrue%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" "${COL_LIGHT_RED}" "${COL_NC}" printf " This check can be skipped by setting the environment variable %bPIHOLE_SELINUX%b to %btrue%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" "${COL_LIGHT_RED}" "${COL_NC}"
printf " e.g: export PIHOLE_SELINUX=true\\n" printf " e.g: export PIHOLE_SELINUX=true\\n"
printf " By setting this variable to true you acknowledge there may be issues with Pi-hole during or after the install\\n" printf " By setting this variable to true you acknowledge there may be issues with Pi-hole during or after the install\\n"
printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
exit 1; exit 1
elif [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -n "${PIHOLE_SELINUX}" ]]; then elif [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -n "${PIHOLE_SELINUX}" ]]; then
printf " %b %bSELinux Enforcing detected%b. PIHOLE_SELINUX env variable set - installer will continue\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b %bSELinux Enforcing detected%b. PIHOLE_SELINUX env variable set - installer will continue\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}"
fi fi
@ -1612,10 +1615,10 @@ checkSelinux() {
displayFinalMessage() { displayFinalMessage() {
# TODO: COME BACK TO THIS, WHAT IS GOING ON? # TODO: COME BACK TO THIS, WHAT IS GOING ON?
# If the number of arguments is > 0, # If the number of arguments is > 0,
if [[ "${#1}" -gt 0 ]] ; then if [[ "${#1}" -gt 0 ]]; then
# set the password to the first argument. # set the password to the first argument.
pwstring="$1" pwstring="$1"
elif [[ $(pihole-FTL --config webserver.api.pwhash) == '""' ]] ; then elif [[ $(pihole-FTL --config webserver.api.pwhash) == '""' ]]; then
# Else if the password exists from previous setup, we'll load it later # Else if the password exists from previous setup, we'll load it later
pwstring="unchanged" pwstring="unchanged"
else else
@ -1626,7 +1629,6 @@ displayFinalMessage() {
# Store a message in a variable and display it # Store a message in a variable and display it
additional="View the web interface at http://pi.hole/admin:${WEBPORT} or http://${IPV4_ADDRESS%/*}:${WEBPORT}/admin\\n\\nYour Admin Webpage login password is ${pwstring}" additional="View the web interface at http://pi.hole/admin:${WEBPORT} or http://${IPV4_ADDRESS%/*}:${WEBPORT}/admin\\n\\nYour Admin Webpage login password is ${pwstring}"
# Final completion message to user # Final completion message to user
dialog --no-shadow --keep-tite \ dialog --no-shadow --keep-tite \
--title "Installation Complete!" \ --title "Installation Complete!" \
@ -1659,7 +1661,7 @@ update_dialogs() {
--title "Existing Install Detected!" \ --title "Existing Install Detected!" \
--menu "\\n\\nWe have detected an existing install.\ --menu "\\n\\nWe have detected an existing install.\
\\n\\nPlease choose from the following options:\ \\n\\nPlease choose from the following options:\
\\n($strAdd)"\ \\n($strAdd)" \
"${r}" "${c}" 2 \ "${r}" "${c}" 2 \
"${opt1a}" "${opt1b}" \ "${opt1a}" "${opt1b}" \
"${opt2a}" "${opt2b}") || result=$? "${opt2a}" "${opt2b}") || result=$?
@ -1691,9 +1693,9 @@ check_download_exists() {
status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1) status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1)
# Check the status code # Check the status code
if grep -q "200" <<< "$status"; then if grep -q "200" <<<"$status"; then
return 0 return 0
elif grep -q "404" <<< "$status"; then elif grep -q "404" <<<"$status"; then
return 1 return 1
fi fi
@ -1724,7 +1726,7 @@ get_available_branches() {
cd "${directory}" || return 1 cd "${directory}" || return 1
# Get reachable remote branches, but store STDERR as STDOUT variable # Get reachable remote branches, but store STDERR as STDOUT variable
output=$( { git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1 ) output=$({ git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1)
# echo status for calling function to capture # echo status for calling function to capture
echo "$output" echo "$output"
return return
@ -1740,7 +1742,7 @@ fetch_checkout_pull_branch() {
# Set the reference for the requested branch, fetch, check it put and pull it # Set the reference for the requested branch, fetch, check it put and pull it
cd "${directory}" || return 1 cd "${directory}" || return 1
git remote set-branches origin "${branch}" || return 1 git remote set-branches origin "${branch}" || return 1
git stash --all --quiet &> /dev/null || true git stash --all --quiet &>/dev/null || true
git clean --quiet --force -d || true git clean --quiet --force -d || true
git fetch --quiet || return 1 git fetch --quiet || return 1
checkout_pull_branch "${directory}" "${branch}" || return 1 checkout_pull_branch "${directory}" "${branch}" || return 1
@ -1777,26 +1779,30 @@ clone_or_update_repos() {
if [[ "${reconfigure}" == true ]]; then if [[ "${reconfigure}" == true ]]; then
printf " %b Performing reconfiguration, skipping download of local repos\\n" "${INFO}" printf " %b Performing reconfiguration, skipping download of local repos\\n" "${INFO}"
# Reset the Core repo # Reset the Core repo
resetRepo ${PI_HOLE_LOCAL_REPO} || \ resetRepo ${PI_HOLE_LOCAL_REPO} ||
{ printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"; \ {
exit 1; \ printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"
exit 1
} }
# Reset the Web repo # Reset the Web repo
resetRepo ${webInterfaceDir} || \ resetRepo ${webInterfaceDir} ||
{ printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}"; \ {
exit 1; \ printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}"
exit 1
} }
# Otherwise, a repair is happening # Otherwise, a repair is happening
else else
# so get git files for Core # so get git files for Core
getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || \ getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} ||
{ printf " %b Unable to clone %s into %s, unable to continue%b\\n" "${COL_LIGHT_RED}" "${piholeGitUrl}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"; \ {
exit 1; \ printf " %b Unable to clone %s into %s, unable to continue%b\\n" "${COL_LIGHT_RED}" "${piholeGitUrl}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"
exit 1
} }
# get the Web git files # get the Web git files
getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || \ getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} ||
{ printf " %b Unable to clone %s into ${webInterfaceDir}, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceGitUrl}" "${COL_NC}"; \ {
exit 1; \ printf " %b Unable to clone %s into ${webInterfaceDir}, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceGitUrl}" "${COL_NC}"
exit 1
} }
fi fi
} }
@ -1810,13 +1816,16 @@ FTLinstall() {
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# Move into the temp ftl directory # Move into the temp ftl directory
pushd "$(mktemp -d)" > /dev/null || { printf "Unable to make temporary directory for FTL binary download\\n"; return 1; } pushd "$(mktemp -d)" >/dev/null || {
printf "Unable to make temporary directory for FTL binary download\\n"
return 1
}
local tempdir local tempdir
tempdir="$(pwd)" tempdir="$(pwd)"
local ftlBranch local ftlBranch
local url local url
if [[ -f "/etc/pihole/ftlbranch" ]];then if [[ -f "/etc/pihole/ftlbranch" ]]; then
ftlBranch=$(</etc/pihole/ftlbranch) ftlBranch=$(</etc/pihole/ftlbranch)
else else
ftlBranch="master" ftlBranch="master"
@ -1826,7 +1835,7 @@ FTLinstall() {
binary="${1}" binary="${1}"
# Determine which version of FTL to download # Determine which version of FTL to download
if [[ "${ftlBranch}" == "master" ]];then if [[ "${ftlBranch}" == "master" ]]; then
url="https://github.com/pi-hole/ftl/releases/latest/download" url="https://github.com/pi-hole/ftl/releases/latest/download"
else else
url="https://ftl.pi-hole.net/${ftlBranch}" url="https://ftl.pi-hole.net/${ftlBranch}"
@ -1844,13 +1853,16 @@ FTLinstall() {
curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true
# Stop pihole-FTL service if available # Stop pihole-FTL service if available
stop_service pihole-FTL &> /dev/null stop_service pihole-FTL &>/dev/null
# Install the new version with the correct permissions # Install the new version with the correct permissions
install -T -m 0755 "${binary}" /usr/bin/pihole-FTL install -T -m 0755 "${binary}" /usr/bin/pihole-FTL
# Move back into the original directory the user was in # Move back into the original directory the user was in
popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; } popd >/dev/null || {
printf "Unable to return to original directory after FTL binary download.\\n"
return 1
}
# Installed the FTL service # Installed the FTL service
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
@ -1861,7 +1873,10 @@ FTLinstall() {
return 0 return 0
else else
# Otherwise, the hash download failed, so print and exit. # Otherwise, the hash download failed, so print and exit.
popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; } popd >/dev/null || {
printf "Unable to return to original directory after FTL binary download.\\n"
return 1
}
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b Error: Download of %s/%s failed (checksum error)%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}" printf " %b Error: Download of %s/%s failed (checksum error)%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}"
@ -1871,7 +1886,10 @@ FTLinstall() {
fi fi
else else
# Otherwise, the download failed, so print and exit. # Otherwise, the download failed, so print and exit.
popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; } popd >/dev/null || {
printf "Unable to return to original directory after FTL binary download.\\n"
return 1
}
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
# The URL could not be found # The URL could not be found
printf " %b Error: URL %s/%s not found%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}" printf " %b Error: URL %s/%s not found%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}"
@ -1884,7 +1902,7 @@ FTLinstall() {
remove_dir() { remove_dir() {
# Delete dir # Delete dir
rm -r "${1}" > /dev/null 2>&1 || \ rm -r "${1}" >/dev/null 2>&1 ||
echo -e " ${CROSS} Unable to remove ${1}" echo -e " ${CROSS} Unable to remove ${1}"
} }
@ -1930,7 +1948,7 @@ get_binary_name() {
elif [[ "${machine}" == "x86_64" ]]; then elif [[ "${machine}" == "x86_64" ]]; then
# This gives the processor of packages dpkg installs (for example, "i386") # This gives the processor of packages dpkg installs (for example, "i386")
local dpkgarch local dpkgarch
dpkgarch=$(dpkg --print-processor 2> /dev/null || dpkg --print-architecture 2> /dev/null) dpkgarch=$(dpkg --print-processor 2>/dev/null || dpkg --print-architecture 2>/dev/null)
# Special case: This is a 32 bit OS, installed on a 64 bit machine # Special case: This is a 32 bit OS, installed on a 64 bit machine
# -> change machine processor to download the 32 bit executable # -> change machine processor to download the 32 bit executable
@ -1971,7 +1989,7 @@ FTLcheckUpdate() {
local ftlBranch local ftlBranch
if [[ -f "/etc/pihole/ftlbranch" ]];then if [[ -f "/etc/pihole/ftlbranch" ]]; then
ftlBranch=$(</etc/pihole/ftlbranch) ftlBranch=$(</etc/pihole/ftlbranch)
else else
ftlBranch="master" ftlBranch="master"
@ -2082,7 +2100,7 @@ make_temporary_log() {
copy_to_install_log() { copy_to_install_log() {
# Copy the contents of file descriptor 3 into the install log # Copy the contents of file descriptor 3 into the install log
# Since we use color codes such as '\e[1;33m', they should be removed # Since we use color codes such as '\e[1;33m', they should be removed
sed 's/\[[0-9;]\{1,5\}m//g' < /proc/$$/fd/3 > "${installLogLoc}" sed 's/\[[0-9;]\{1,5\}m//g' </proc/$$/fd/3 >"${installLogLoc}"
chmod 644 "${installLogLoc}" chmod 644 "${installLogLoc}"
chown pihole:pihole "${installLogLoc}" chown pihole:pihole "${installLogLoc}"
} }
@ -2110,7 +2128,7 @@ main() {
printf " %b Sudo utility check" "${INFO}" printf " %b Sudo utility check" "${INFO}"
# If the sudo command exists, try rerunning as admin # If the sudo command exists, try rerunning as admin
if is_command sudo ; then if is_command sudo; then
printf "%b %b Sudo utility check\\n" "${OVER}" "${TICK}" printf "%b %b Sudo utility check\\n" "${OVER}" "${TICK}"
# when run via curl piping # when run via curl piping
@ -2230,16 +2248,16 @@ main() {
# Add password to web UI if there is none # Add password to web UI if there is none
pw="" pw=""
# If no password is set, # If no password is set,
if [[ $(pihole-FTL --config webserver.api.pwhash) == '""' ]] ; then if [[ $(pihole-FTL --config webserver.api.pwhash) == '""' ]]; then
# generate a random password # generate a random password
pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) pw=$(tr -dc _A-Z-a-z-0-9 </dev/urandom | head -c 8)
pihole -a -p "${pw}" pihole -a -p "${pw}"
fi fi
# Check for and disable systemd-resolved-DNSStubListener before reloading resolved # Check for and disable systemd-resolved-DNSStubListener before reloading resolved
# DNSStubListener needs to remain in place for installer to download needed files, # DNSStubListener needs to remain in place for installer to download needed files,
# so this change needs to be made after installation is complete, # so this change needs to be made after installation is complete,
# but before starting or restarting the ftl service # but before starting or resttarting the ftl service
disable_resolved_stublistener disable_resolved_stublistener
# Check if gravity database needs to be upgraded. If so, do it without rebuilding # Check if gravity database needs to be upgraded. If so, do it without rebuilding
@ -2262,7 +2280,7 @@ main() {
# can be removed with Pi-hole v6.0 # can be removed with Pi-hole v6.0
# To be sure FTL is not running when we move the files we explicitly stop it here # To be sure FTL is not running when we move the files we explicitly stop it here
stop_service pihole-FTL &> /dev/null stop_service pihole-FTL &>/dev/null
if [ ! -d /var/log/pihole/ ]; then if [ ! -d /var/log/pihole/ ]; then
mkdir -m 0755 /var/log/pihole/ mkdir -m 0755 /var/log/pihole/
@ -2276,7 +2294,7 @@ main() {
# /var/log/pihole-FTL.log.3.gz -> /var/log/pihole/FTL.log.3.gz # /var/log/pihole-FTL.log.3.gz -> /var/log/pihole/FTL.log.3.gz
# /var/log/pihole-FTL.log.4.gz -> /var/log/pihole/FTL.log.4.gz # /var/log/pihole-FTL.log.4.gz -> /var/log/pihole/FTL.log.4.gz
# /var/log/pihole-FTL.log.5.gz -> /var/log/pihole/FTL.log.5.gz # /var/log/pihole-FTL.log.5.gz -> /var/log/pihole/FTL.log.5.gz
for f in /var/log/pihole-FTL.log*; do mv "$f" "$( sed "s/pihole-/pihole\//" <<< "$f")"; done for f in /var/log/pihole-FTL.log*; do mv "$f" "$(sed "s/pihole-/pihole\//" <<<"$f")"; done
fi fi
# Remaining log files # Remaining log files
@ -2297,7 +2315,7 @@ main() {
fi fi
# If there is a password # If there is a password
if (( ${#pw} > 0 )) ; then if ((${#pw} > 0)); then
# display the password # display the password
printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${pw}" "${COL_NC}" printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${pw}" "${COL_NC}"
printf " %b This can be changed using 'pihole -a -p'\\n\\n" "${INFO}" printf " %b This can be changed using 'pihole -a -p'\\n\\n" "${INFO}"
@ -2328,6 +2346,6 @@ main() {
} }
# allow to source this script without running it # allow to source this script without running it
if [[ "${SKIP_INSTALL}" != true ]] ; then if [[ "${SKIP_INSTALL}" != true ]]; then
main "$@" main "$@"
fi fi

View file

@ -60,7 +60,7 @@ gravityOLDfile="${gravityDIR}/gravity_old.db"
# Generate new SQLite3 file from schema template # Generate new SQLite3 file from schema template
generate_gravity_database() { generate_gravity_database() {
if ! pihole-FTL sqlite3 -ni "${gravityDBfile}" < "${gravityDBschema}"; then if ! pihole-FTL sqlite3 -ni "${gravityDBfile}" <"${gravityDBschema}"; then
echo -e " ${CROSS} Unable to create ${gravityDBfile}" echo -e " ${CROSS} Unable to create ${gravityDBfile}"
return 1 return 1
fi fi
@ -75,7 +75,7 @@ gravity_build_tree() {
echo -ne " ${INFO} ${str}..." echo -ne " ${INFO} ${str}..."
# The index is intentionally not UNIQUE as poor quality adlists may contain domains more than once # The index is intentionally not UNIQUE as poor quality adlists may contain domains more than once
output=$( { pihole-FTL sqlite3 -ni "${gravityTEMPfile}" "CREATE INDEX idx_gravity ON gravity (domain, adlist_id);"; } 2>&1 ) output=$({ pihole-FTL sqlite3 -ni "${gravityTEMPfile}" "CREATE INDEX idx_gravity ON gravity (domain, adlist_id);"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -114,7 +114,7 @@ gravity_swap_databases() {
# Update timestamp when the gravity table was last updated successfully # Update timestamp when the gravity table was last updated successfully
update_gravity_timestamp() { update_gravity_timestamp() {
output=$( { printf ".timeout 30000\\nINSERT OR REPLACE INTO info (property,value) values ('updated',cast(strftime('%%s', 'now') as int));" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1 ) output=$({ printf ".timeout 30000\\nINSERT OR REPLACE INTO info (property,value) values ('updated',cast(strftime('%%s', 'now') as int));" | pihole-FTL sqlite3 -ni -ni "${gravityTEMPfile}"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -168,19 +168,18 @@ database_table_from_file() {
# Loop over all domains in ${src} file # Loop over all domains in ${src} file
# Read file line by line # Read file line by line
grep -v '^ *#' < "${src}" | while IFS= read -r domain grep -v '^ *#' <"${src}" | while IFS= read -r domain; do
do
# Only add non-empty lines # Only add non-empty lines
if [[ -n "${domain}" ]]; then if [[ -n "${domain}" ]]; then
if [[ "${table}" == "domain_audit" ]]; then if [[ "${table}" == "domain_audit" ]]; then
# domain_audit table format (no enable or modified fields) # domain_audit table format (no enable or modified fields)
echo "${rowid},\"${domain}\",${timestamp}" >> "${tmpFile}" echo "${rowid},\"${domain}\",${timestamp}" >>"${tmpFile}"
elif [[ "${table}" == "adlist" ]]; then elif [[ "${table}" == "adlist" ]]; then
# Adlist table format # Adlist table format
echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${src}\",,0,0,0,0,0" >> "${tmpFile}" echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${src}\",,0,0,0,0,0" >>"${tmpFile}"
else else
# White-, black-, and regexlist table format # White-, black-, and regexlist table format
echo "${rowid},${list_type},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${src}\"" >> "${tmpFile}" echo "${rowid},${list_type},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${src}\"" >>"${tmpFile}"
fi fi
rowid+=1 rowid+=1
fi fi
@ -189,7 +188,7 @@ database_table_from_file() {
# Store domains in database table specified by ${table} # Store domains in database table specified by ${table}
# Use printf as .mode and .import need to be on separate lines # Use printf as .mode and .import need to be on separate lines
# see https://unix.stackexchange.com/a/445615/83260 # see https://unix.stackexchange.com/a/445615/83260
output=$( { printf ".timeout 30000\\n.mode csv\\n.import \"%s\" %s\\n" "${tmpFile}" "${table}" | pihole-FTL sqlite3 -ni "${gravityDBfile}"; } 2>&1 ) output=$({ printf ".timeout 30000\\n.mode csv\\n.import \"%s\" %s\\n" "${tmpFile}" "${table}" | pihole-FTL sqlite3 -ni "${gravityDBfile}"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -199,17 +198,17 @@ database_table_from_file() {
# Move source file to backup directory, create directory if not existing # Move source file to backup directory, create directory if not existing
mkdir -p "${backup_path}" mkdir -p "${backup_path}"
mv "${src}" "${backup_file}" 2> /dev/null || \ mv "${src}" "${backup_file}" 2>/dev/null ||
echo -e " ${CROSS} Unable to backup ${src} to ${backup_path}" echo -e " ${CROSS} Unable to backup ${src} to ${backup_path}"
# Delete tmpFile # Delete tmpFile
rm "${tmpFile}" > /dev/null 2>&1 || \ rm "${tmpFile}" >/dev/null 2>&1 ||
echo -e " ${CROSS} Unable to remove ${tmpFile}" echo -e " ${CROSS} Unable to remove ${tmpFile}"
} }
# Check if a column with name ${2} exists in gravity table with name ${1} # Check if a column with name ${2} exists in gravity table with name ${1}
gravity_column_exists() { gravity_column_exists() {
output=$( { printf ".timeout 30000\\nSELECT EXISTS(SELECT * FROM pragma_table_info('%s') WHERE name='%s');\\n" "${1}" "${2}" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1 ) output=$({ printf ".timeout 30000\\nSELECT EXISTS(SELECT * FROM pragma_table_info('%s') WHERE name='%s');\\n" "${1}" "${2}" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1)
if [[ "${output}" == "1" ]]; then if [[ "${output}" == "1" ]]; then
return 0 # Bash 0 is success return 0 # Bash 0 is success
fi fi
@ -221,10 +220,10 @@ gravity_column_exists() {
database_adlist_number() { database_adlist_number() {
# Only try to set number of domains when this field exists in the gravity database # Only try to set number of domains when this field exists in the gravity database
if ! gravity_column_exists "adlist" "number"; then if ! gravity_column_exists "adlist" "number"; then
return; return
fi fi
output=$( { printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${2}" "${3}" "${1}" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1 ) output=$({ printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${2}" "${3}" "${1}" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -237,10 +236,10 @@ database_adlist_number() {
database_adlist_status() { database_adlist_status() {
# Only try to set the status when this field exists in the gravity database # Only try to set the status when this field exists in the gravity database
if ! gravity_column_exists "adlist" "status"; then if ! gravity_column_exists "adlist" "status"; then
return; return
fi fi
output=$( { printf ".timeout 30000\\nUPDATE adlist SET status = %i WHERE id = %i;\\n" "${2}" "${1}" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1 ) output=$({ printf ".timeout 30000\\nUPDATE adlist SET status = %i WHERE id = %i;\\n" "${2}" "${1}" | pihole-FTL sqlite3 -ni "${gravityTEMPfile}"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -297,7 +296,7 @@ gravity_CheckDNSResolutionAvailable() {
local lookupDomain="raw.githubusercontent.com" local lookupDomain="raw.githubusercontent.com"
# Determine if $lookupDomain is resolvable # Determine if $lookupDomain is resolvable
if timeout 4 getent hosts "${lookupDomain}" &> /dev/null; then if timeout 4 getent hosts "${lookupDomain}" &>/dev/null; then
# Print confirmation of resolvability if it had previously failed # Print confirmation of resolvability if it had previously failed
if [[ -n "${secs:-}" ]]; then if [[ -n "${secs:-}" ]]; then
echo -e "${OVER} ${TICK} DNS resolution is now available\\n" echo -e "${OVER} ${TICK} DNS resolution is now available\\n"
@ -311,7 +310,7 @@ gravity_CheckDNSResolutionAvailable() {
# If the /etc/resolv.conf contains resolvers other than 127.0.0.1 then the local dnsmasq will not be queried and pi.hole is NXDOMAIN. # If the /etc/resolv.conf contains resolvers other than 127.0.0.1 then the local dnsmasq will not be queried and pi.hole is NXDOMAIN.
# This means that even though name resolution is working, the getent hosts check fails and the holddown timer keeps ticking and eventually fails # This means that even though name resolution is working, the getent hosts check fails and the holddown timer keeps ticking and eventually fails
# So we check the output of the last command and if it failed, attempt to use dig +short as a fallback # So we check the output of the last command and if it failed, attempt to use dig +short as a fallback
if timeout 4 dig +short "${lookupDomain}" &> /dev/null; then if timeout 4 dig +short "${lookupDomain}" &>/dev/null; then
if [[ -n "${secs:-}" ]]; then if [[ -n "${secs:-}" ]]; then
echo -e "${OVER} ${TICK} DNS resolution is now available\\n" echo -e "${OVER} ${TICK} DNS resolution is now available\\n"
fi fi
@ -322,7 +321,7 @@ gravity_CheckDNSResolutionAvailable() {
fi fi
# Determine error output message # Determine error output message
if pgrep pihole-FTL &> /dev/null; then if pgrep pihole-FTL &>/dev/null; then
echo -e " ${CROSS} DNS resolution is currently unavailable" echo -e " ${CROSS} DNS resolution is currently unavailable"
else else
echo -e " ${CROSS} DNS service is not running" echo -e " ${CROSS} DNS service is not running"
@ -332,7 +331,7 @@ gravity_CheckDNSResolutionAvailable() {
# Ensure DNS server is given time to be resolvable # Ensure DNS server is given time to be resolvable
secs="120" secs="120"
echo -ne " ${INFO} Time until retry: ${secs}" echo -ne " ${INFO} Time until retry: ${secs}"
until timeout 1 getent hosts "${lookupDomain}" &> /dev/null; do until timeout 1 getent hosts "${lookupDomain}" &>/dev/null; do
[[ "${secs:-}" -eq 0 ]] && break [[ "${secs:-}" -eq 0 ]] && break
echo -ne "${OVER} ${INFO} Time until retry: ${secs}" echo -ne "${OVER} ${INFO} Time until retry: ${secs}"
: $((secs--)) : $((secs--))
@ -353,19 +352,19 @@ gravity_DownloadBlocklists() {
# Retrieve source URLs from gravity database # Retrieve source URLs from gravity database
# We source only enabled adlists, SQLite3 stores boolean values as 0 (false) or 1 (true) # 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 sources <<<"$(pihole-FTL sqlite3 -ni -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 sourceIDs <<<"$(pihole-FTL sqlite3 -ni -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)" mapfile -t sourceTypes <<<"$(pihole-FTL sqlite3 -ni "${gravityDBfile}" "SELECT type FROM vw_adlist;" 2>/dev/null)"
# Parse source domains from $sources # Parse source domains from $sources
mapfile -t sourceDomains <<< "$( mapfile -t sourceDomains <<<"$(
# Logic: Split by folder/port # Logic: Split by folder/port
awk -F '[/:]' '{ awk -F '[/:]' '{
# Remove URL protocol & optional username:password@ # Remove URL protocol & optional username:password@
gsub(/(.*:\/\/|.*:.*@)/, "", $0) gsub(/(.*:\/\/|.*:.*@)/, "", $0)
if(length($1)>0){print $1} if(length($1)>0){print $1}
else {print "local"} else {print "local"}
}' <<< "$(printf '%s\n' "${sources[@]}")" 2> /dev/null }' <<<"$(printf '%s\n' "${sources[@]}")" 2>/dev/null
)" )"
local str="Pulling blocklist source list into range" local str="Pulling blocklist source list into range"
@ -383,8 +382,8 @@ gravity_DownloadBlocklists() {
# Prepare new gravity database # Prepare new gravity database
str="Preparing new gravity database" str="Preparing new gravity database"
echo -ne " ${INFO} ${str}..." echo -ne " ${INFO} ${str}..."
rm "${gravityTEMPfile}" > /dev/null 2>&1 rm "${gravityTEMPfile}" >/dev/null 2>&1
output=$( { pihole-FTL sqlite3 -ni "${gravityTEMPfile}" < "${gravityDBschema}"; } 2>&1 ) output=$({ pihole-FTL sqlite3 -ni "${gravityTEMPfile}" <"${gravityDBschema}"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -404,7 +403,7 @@ gravity_DownloadBlocklists() {
copyGravity="${copyGravity//"${gravityDBfile_default}"/"${gravityDBfile}"}" copyGravity="${copyGravity//"${gravityDBfile_default}"/"${gravityDBfile}"}"
fi fi
output=$( { pihole-FTL sqlite3 -ni "${gravityTEMPfile}" <<< "${copyGravity}"; } 2>&1 ) output=$({ pihole-FTL sqlite3 -ni "${gravityTEMPfile}" <<<"${copyGravity}"; } 2>&1)
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -449,7 +448,7 @@ gravity_DownloadBlocklists() {
# this will remove first @ that is after schema and before domain # this will remove first @ that is after schema and before domain
# \1 is optional schema, \2 is userinfo # \1 is optional schema, \2 is userinfo
check_url="$( sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<< "$url" )" check_url="$(sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<<"$url")"
if [[ "${check_url}" =~ ${regex} ]]; then if [[ "${check_url}" =~ ${regex} ]]; then
echo -e " ${CROSS} Invalid Target" echo -e " ${CROSS} Invalid Target"
@ -469,7 +468,7 @@ compareLists() {
if [[ -s "${target}.sha1" ]]; then if [[ -s "${target}.sha1" ]]; then
if ! sha1sum --check --status --strict "${target}.sha1"; then if ! sha1sum --check --status --strict "${target}.sha1"; then
# The list changed upstream, we need to update the checksum # The list changed upstream, we need to update the checksum
sha1sum "${target}" > "${target}.sha1" sha1sum "${target}" >"${target}.sha1"
echo " ${INFO} List has been updated" echo " ${INFO} List has been updated"
database_adlist_status "${adlistID}" "1" database_adlist_status "${adlistID}" "1"
else else
@ -478,7 +477,7 @@ compareLists() {
fi fi
else else
# No checksum available, create one for comparing on the next run # No checksum available, create one for comparing on the next run
sha1sum "${target}" > "${target}.sha1" sha1sum "${target}" >"${target}.sha1"
# We assume here it was changed upstream # We assume here it was changed upstream
database_adlist_status "${adlistID}" "1" database_adlist_status "${adlistID}" "1"
fi fi
@ -507,25 +506,29 @@ gravity_DownloadBlocklistFromUrl() {
echo -ne " ${INFO} ${str} Pending..." echo -ne " ${INFO} ${str} Pending..."
blocked=false blocked=false
case $(getFTLConfigValue dns.blocking.mode) in case $(getFTLConfigValue dns.blocking.mode) in
"IP-NODATA-AAAA"|"IP") "IP-NODATA-AAAA" | "IP")
# Get IP address of this domain # Get IP address of this domain
ip="$(dig "${domain}" +short)" ip="$(dig "${domain}" +short)"
# Check if this IP matches any IP of the system # Check if this IP matches any IP of the system
if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<<"$(ip a)") -gt 0 ]]; then
blocked=true blocked=true
fi;; fi
;;
"NXDOMAIN") "NXDOMAIN")
if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then
blocked=true blocked=true
fi;; fi
;;
"NODATA") "NODATA")
if [[ $(dig "${domain}" | grep "NOERROR" -c) -ge 1 ]] && [[ -z $(dig +short "${domain}") ]]; then if [[ $(dig "${domain}" | grep "NOERROR" -c) -ge 1 ]] && [[ -z $(dig +short "${domain}") ]]; then
blocked=true blocked=true
fi;; fi
"NULL"|*) ;;
"NULL" | *)
if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then
blocked=true blocked=true
fi;; fi
;;
esac esac
if [[ "${blocked}" == true ]]; then if [[ "${blocked}" == true ]]; then
@ -537,43 +540,53 @@ gravity_DownloadBlocklistFromUrl() {
fi fi
ip=$(dig "@${ip_addr}" -p "${port}" +short "${domain}" | tail -1) ip=$(dig "@${ip_addr}" -p "${port}" +short "${domain}" | tail -1)
if [[ $(echo "${url}" | awk -F '://' '{print $1}') = "https" ]]; then if [[ $(echo "${url}" | awk -F '://' '{print $1}') = "https" ]]; then
port=443; port=443
else port=80 else
port=80
fi fi
bad_list=$(pihole -q -adlist "${domain}" | head -n1 | awk -F 'Match found in ' '{print $2}') bad_list=$(pihole -q -adlist "${domain}" | head -n1 | awk -F 'Match found in ' '{print $2}')
echo -e "${OVER} ${CROSS} ${str} ${domain} is blocked by ${bad_list%:}. Using DNS on ${PIHOLE_DNS_1} to download ${url}"; echo -e "${OVER} ${CROSS} ${str} ${domain} is blocked by ${bad_list%:}. Using DNS on ${PIHOLE_DNS_1} to download ${url}"
echo -ne " ${INFO} ${str} Pending..." echo -ne " ${INFO} ${str} Pending..."
cmd_ext="--resolve $domain:$port:$ip" cmd_ext="--resolve $domain:$port:$ip"
fi fi
# shellcheck disable=SC2086 # shellcheck disable=SC2086
httpCode=$(curl --connect-timeout ${curl_connect_timeout} -s -L ${compression} ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" "${url}" -o "${listCurlBuffer}" 2> /dev/null) httpCode=$(curl --connect-timeout ${curl_connect_timeout} -s -L ${compression} ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" "${url}" -o "${listCurlBuffer}" 2>/dev/null)
case $url in case $url in
# Did we "download" a local file? # Did we "download" a local file?
"file"*) "file"*)
if [[ -s "${listCurlBuffer}" ]]; then if [[ -s "${listCurlBuffer}" ]]; then
echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true echo -e "${OVER} ${TICK} ${str} Retrieval successful"
success=true
else else
echo -e "${OVER} ${CROSS} ${str} Not found / empty list" echo -e "${OVER} ${CROSS} ${str} Not found / empty list"
fi;; fi
;;
# Did we "download" a remote file? # Did we "download" a remote file?
*) *)
# Determine "Status:" output based on HTTP response # Determine "Status:" output based on HTTP response
case "${httpCode}" in case "${httpCode}" in
"200") echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true;; "200")
"304") echo -e "${OVER} ${TICK} ${str} No changes detected"; success=true;; echo -e "${OVER} ${TICK} ${str} Retrieval successful"
"000") echo -e "${OVER} ${CROSS} ${str} Connection Refused";; success=true
"403") echo -e "${OVER} ${CROSS} ${str} Forbidden";; ;;
"404") echo -e "${OVER} ${CROSS} ${str} Not found";; "304")
"408") echo -e "${OVER} ${CROSS} ${str} Time-out";; echo -e "${OVER} ${TICK} ${str} No changes detected"
"451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons";; success=true
"500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error";; ;;
"504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)";; "000") echo -e "${OVER} ${CROSS} ${str} Connection Refused" ;;
"521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)";; "403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;;
"522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)";; "404") echo -e "${OVER} ${CROSS} ${str} Not found" ;;
* ) echo -e "${OVER} ${CROSS} ${str} ${url} (${httpCode})";; "408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;;
esac;; "451") echo -e "${OVER} ${CROSS} ${str} Unavailable For Legal Reasons" ;;
"500") echo -e "${OVER} ${CROSS} ${str} Internal Server Error" ;;
"504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;;
"521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;;
"522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;;
*) echo -e "${OVER} ${CROSS} ${str} ${url} (${httpCode})" ;;
esac
;;
esac esac
local done="false" local done="false"
@ -627,7 +640,7 @@ gravity_ParseFileIntoDomains() {
# This helps with that and makes it easier to read # This helps with that and makes it easier to read
# It also helps with debugging so each stage of the script can be researched more in depth # It also helps with debugging so each stage of the script can be researched more in depth
# 1) Convert all characters to lowercase # 1) Convert all characters to lowercase
tr '[:upper:]' '[:lower:]' < "${src}" > "${destination}" tr '[:upper:]' '[:lower:]' <"${src}" >"${destination}"
# 2) Remove carriage returns # 2) Remove carriage returns
# 3) Remove lines starting with ! (ABP Comments) # 3) Remove lines starting with ! (ABP Comments)
@ -654,12 +667,12 @@ gravity_Table_Count() {
local table="${1}" local table="${1}"
local str="${2}" local str="${2}"
local num local num
num="$(pihole-FTL sqlite3 -ni "${gravityTEMPfile}" "SELECT COUNT(*) FROM ${table};")" num="$(pihole-FTL sqlite3 -ni -ni "${gravityTEMPfile}" "SELECT COUNT(*) FROM ${table};")"
if [[ "${table}" == "gravity" ]]; then if [[ "${table}" == "gravity" ]]; then
local unique local unique
unique="$(pihole-FTL sqlite3 -ni "${gravityTEMPfile}" "SELECT COUNT(*) FROM (SELECT DISTINCT domain FROM ${table});")" unique="$(pihole-FTL sqlite3 -ni -ni "${gravityTEMPfile}" "SELECT COUNT(*) FROM (SELECT DISTINCT domain FROM ${table});")"
echo -e " ${INFO} Number of ${str}: ${num} (${COL_BOLD}${unique} unique domains${COL_NC})" echo -e " ${INFO} Number of ${str}: ${num} (${COL_BOLD}${unique} unique domains${COL_NC})"
pihole-FTL sqlite3 -ni "${gravityTEMPfile}" "INSERT OR REPLACE INTO info (property,value) VALUES ('gravity_count',${unique});" pihole-FTL sqlite3 -ni -ni "${gravityTEMPfile}" "INSERT OR REPLACE INTO info (property,value) VALUES ('gravity_count',${unique});"
else else
echo -e " ${INFO} Number of ${str}: ${num}" echo -e " ${INFO} Number of ${str}: ${num}"
fi fi
@ -689,12 +702,12 @@ gravity_Cleanup() {
echo -ne " ${INFO} ${str}..." echo -ne " ${INFO} ${str}..."
# Delete tmp content generated by Gravity # Delete tmp content generated by Gravity
rm ${piholeDir}/pihole.*.txt 2> /dev/null rm ${piholeDir}/pihole.*.txt 2>/dev/null
rm ${piholeDir}/*.tmp 2> /dev/null rm ${piholeDir}/*.tmp 2>/dev/null
# listCurlBuffer location # listCurlBuffer location
rm "${GRAVITY_TMPDIR}"/*.phgpb 2> /dev/null rm "${GRAVITY_TMPDIR}"/*.phgpb 2>/dev/null
# invalid_domains location # invalid_domains location
rm "${GRAVITY_TMPDIR}"/*.ph-non-domains 2> /dev/null rm "${GRAVITY_TMPDIR}"/*.ph-non-domains 2>/dev/null
# Ensure this function only runs when gravity_SetDownloadOptions() has completed # Ensure this function only runs when gravity_SetDownloadOptions() has completed
if [[ "${gravity_Blackbody:-}" == true ]]; then if [[ "${gravity_Blackbody:-}" == true ]]; then
@ -702,7 +715,7 @@ gravity_Cleanup() {
for file in "${piholeDir}"/*."${domainsExtension}"; do for file in "${piholeDir}"/*."${domainsExtension}"; do
# If list is not in active array, then remove it # If list is not in active array, then remove it
if [[ ! "${activeDomains[*]}" == *"${file}"* ]]; then if [[ ! "${activeDomains[*]}" == *"${file}"* ]]; then
rm -f "${file}" 2> /dev/null || \ rm -f "${file}" 2>/dev/null ||
echo -e " ${CROSS} Failed to remove ${file##*/}" echo -e " ${CROSS} Failed to remove ${file##*/}"
fi fi
done done
@ -744,17 +757,17 @@ database_recovery() {
fi fi
else else
echo -e "${OVER} ${CROSS} ${str} - errors found:" echo -e "${OVER} ${CROSS} ${str} - errors found:"
while IFS= read -r line ; do echo " - $line"; done <<< "$result" while IFS= read -r line; do echo " - $line"; done <<<"$result"
fi fi
else else
echo -e "${OVER} ${CROSS} ${str} - errors found:" echo -e "${OVER} ${CROSS} ${str} - errors found:"
while IFS= read -r line ; do echo " - $line"; done <<< "$result" while IFS= read -r line; do echo " - $line"; done <<<"$result"
fi fi
str="Trying to recover existing gravity database" str="Trying to recover existing gravity database"
echo -ne " ${INFO} ${str}..." echo -ne " ${INFO} ${str}..."
# We have to remove any possibly existing recovery database or this will fail # We have to remove any possibly existing recovery database or this will fail
rm -f "${gravityDBfile}.recovered" > /dev/null 2>&1 rm -f "${gravityDBfile}.recovered" >/dev/null 2>&1
if result="$(pihole-FTL sqlite3 -ni "${gravityDBfile}" ".recover" | pihole-FTL sqlite3 -ni "${gravityDBfile}.recovered" 2>&1)"; then if result="$(pihole-FTL sqlite3 -ni "${gravityDBfile}" ".recover" | pihole-FTL sqlite3 -ni "${gravityDBfile}.recovered" 2>&1)"; then
echo -e "${OVER} ${TICK} ${str} - success" echo -e "${OVER} ${TICK} ${str} - success"
mv "${gravityDBfile}" "${gravityDBfile}.old" mv "${gravityDBfile}" "${gravityDBfile}.old"
@ -763,7 +776,7 @@ database_recovery() {
echo -ne " ${INFO} The old ${gravityDBfile} has been moved to ${gravityDBfile}.old" echo -ne " ${INFO} The old ${gravityDBfile} has been moved to ${gravityDBfile}.old"
else else
echo -e "${OVER} ${CROSS} ${str} - the following errors happened:" echo -e "${OVER} ${CROSS} ${str} - the following errors happened:"
while IFS= read -r line ; do echo " - $line"; done <<< "$result" while IFS= read -r line; do echo " - $line"; done <<<"$result"
echo -e " ${CROSS} Recovery failed. Try \"pihole -r recreate\" instead." echo -e " ${CROSS} Recovery failed. Try \"pihole -r recreate\" instead."
exit 1 exit 1
fi fi
@ -782,9 +795,10 @@ Options:
repairSelector() { repairSelector() {
case "$1" in case "$1" in
"recover") recover_database=true;; "recover") recover_database=true ;;
"recreate") recreate_database=true;; "recreate") recreate_database=true ;;
*) echo "Usage: pihole -g -r {recover,recreate} *)
echo "Usage: pihole -g -r {recover,recreate}
Attempt to repair gravity database Attempt to repair gravity database
Available options: Available options:
@ -803,7 +817,8 @@ Available options:
and create a new file from scratch. If you still and create a new file from scratch. If you still
have the migration backup created when migrating have the migration backup created when migrating
to Pi-hole v5.0, Pi-hole will import these files." to Pi-hole v5.0, Pi-hole will import these files."
exit 0;; exit 0
;;
esac esac
} }
@ -828,9 +843,9 @@ if [[ "${recreate_database:-}" == true ]]; then
str="Recreating gravity database from migration backup" str="Recreating gravity database from migration backup"
echo -ne "${INFO} ${str}..." echo -ne "${INFO} ${str}..."
rm "${gravityDBfile}" rm "${gravityDBfile}"
pushd "${piholeDir}" > /dev/null || exit pushd "${piholeDir}" >/dev/null || exit
cp migration_backup/* . cp migration_backup/* .
popd > /dev/null || exit popd >/dev/null || exit
echo -e "${OVER} ${TICK} ${str}" echo -e "${OVER} ${TICK} ${str}"
fi fi
@ -848,7 +863,7 @@ if [[ "${forceDelete:-}" == true ]]; then
str="Deleting existing list cache" str="Deleting existing list cache"
echo -ne "${INFO} ${str}..." echo -ne "${INFO} ${str}..."
rm /etc/pihole/list.* 2> /dev/null || true rm /etc/pihole/list.* 2>/dev/null || true
echo -e "${OVER} ${TICK} ${str}" echo -e "${OVER} ${TICK} ${str}"
fi fi