Merge branch 'master' into tweak/update
Some checks are pending
Check for merge conflicts / main (push) Waiting to run
CodeQL / Analyze (pull_request) Waiting to run
Test Supported Distributions / smoke-tests (pull_request) Waiting to run
Test Supported Distributions / distro-test (alpine_3_21) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (alpine_3_22) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (alpine_3_23) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (centos_10) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (centos_9) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (debian_11) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (debian_12) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (debian_13) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_40) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_41) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_42) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_43) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (ubuntu_20) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (ubuntu_22) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (ubuntu_24) (pull_request) Blocked by required conditions
Check for merge conflicts / main (pull_request_target) Waiting to run
Some checks are pending
Check for merge conflicts / main (push) Waiting to run
CodeQL / Analyze (pull_request) Waiting to run
Test Supported Distributions / smoke-tests (pull_request) Waiting to run
Test Supported Distributions / distro-test (alpine_3_21) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (alpine_3_22) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (alpine_3_23) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (centos_10) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (centos_9) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (debian_11) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (debian_12) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (debian_13) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_40) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_41) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_42) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (fedora_43) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (ubuntu_20) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (ubuntu_22) (pull_request) Blocked by required conditions
Test Supported Distributions / distro-test (ubuntu_24) (pull_request) Blocked by required conditions
Check for merge conflicts / main (pull_request_target) Waiting to run
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC2034 # Disable warning about unused variables
|
||||
|
||||
# Determine if terminal is capable of showing colors
|
||||
if ([ -t 1 ] && [ $(tput colors) -ge 8 ]) || [ "${WEBCALL}" ]; then
|
||||
# When COL_TABLE is sourced via gravity invoked by FTL, FORCE_COLOR is set to true
|
||||
if { [ -t 1 ] && [ "$(tput colors)" -ge 8 ]; } || [ "${FORCE_COLOR}" ]; then
|
||||
# Bold and underline may not show up on all clients
|
||||
# If something MUST be emphasized, use both
|
||||
COL_BOLD='[1m'
|
||||
COL_ULINE='[4m'
|
||||
|
||||
COL_NC='[0m'
|
||||
COL_GRAY='[90m'
|
||||
COL_RED='[91m'
|
||||
@@ -16,8 +18,6 @@ if ([ -t 1 ] && [ $(tput colors) -ge 8 ]) || [ "${WEBCALL}" ]; then
|
||||
else
|
||||
# Provide empty variables for `set -u`
|
||||
COL_BOLD=""
|
||||
COL_ULINE=""
|
||||
|
||||
COL_NC=""
|
||||
COL_GRAY=""
|
||||
COL_RED=""
|
||||
@@ -28,22 +28,8 @@ else
|
||||
COL_CYAN=""
|
||||
fi
|
||||
|
||||
# Deprecated variables
|
||||
COL_WHITE="${COL_BOLD}"
|
||||
COL_BLACK="${COL_NC}"
|
||||
COL_LIGHT_BLUE="${COL_BLUE}"
|
||||
COL_LIGHT_GREEN="${COL_GREEN}"
|
||||
COL_LIGHT_CYAN="${COL_CYAN}"
|
||||
COL_LIGHT_RED="${COL_RED}"
|
||||
COL_URG_RED="${COL_RED}${COL_BOLD}${COL_ULINE}"
|
||||
COL_LIGHT_PURPLE="${COL_PURPLE}"
|
||||
COL_BROWN="${COL_YELLOW}"
|
||||
COL_LIGHT_GRAY="${COL_GRAY}"
|
||||
COL_DARK_GRAY="${COL_GRAY}"
|
||||
|
||||
TICK="[${COL_GREEN}✓${COL_NC}]"
|
||||
CROSS="[${COL_RED}✗${COL_NC}]"
|
||||
INFO="[i]"
|
||||
QST="[?]"
|
||||
DONE="${COL_GREEN} done!${COL_NC}"
|
||||
OVER="\\r[K"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC3043 #https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
@@ -20,13 +19,20 @@
|
||||
|
||||
TestAPIAvailability() {
|
||||
|
||||
local chaos_api_list authResponse authStatus authData apiAvailable DNSport
|
||||
|
||||
# as we are running locally, we can get the port value from FTL directly
|
||||
local chaos_api_list availabilityResponse
|
||||
PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck source=./advanced/Scripts/utils.sh
|
||||
. "${utilsfile}"
|
||||
|
||||
DNSport=$(getFTLConfigValue dns.port)
|
||||
|
||||
# Query the API URLs from FTL using CHAOS TXT local.api.ftl
|
||||
# The result is a space-separated enumeration of full URLs
|
||||
# e.g., "http://localhost:80/api/" "https://localhost:443/api/"
|
||||
chaos_api_list="$(dig +short chaos txt local.api.ftl @127.0.0.1)"
|
||||
chaos_api_list="$(dig +short -p "${DNSport}" chaos txt local.api.ftl @127.0.0.1)"
|
||||
|
||||
# If the query was not successful, the variable is empty
|
||||
if [ -z "${chaos_api_list}" ]; then
|
||||
@@ -48,39 +54,50 @@ TestAPIAvailability() {
|
||||
API_URL="${API_URL%\"}"
|
||||
API_URL="${API_URL#\"}"
|
||||
|
||||
# Test if the API is available at this URL
|
||||
availabilityResponse=$(curl -skS -o /dev/null -w "%{http_code}" "${API_URL}auth")
|
||||
# Test if the API is available at this URL, include delimiter for ease in splitting payload
|
||||
authResponse=$(curl --connect-timeout 2 -skS -w ">>%{http_code}" "${API_URL}auth")
|
||||
|
||||
# authStatus is the response http_code, eg. 200, 401.
|
||||
# Shell parameter expansion, remove everything up to and including the >> delim
|
||||
authStatus=${authResponse#*>>}
|
||||
# data is everything from response
|
||||
# Shell parameter expansion, remove the >> delim and everything after
|
||||
authData=${authResponse%>>*}
|
||||
|
||||
# Test if http status code was 200 (OK) or 401 (authentication required)
|
||||
if [ ! "${availabilityResponse}" = 200 ] && [ ! "${availabilityResponse}" = 401 ]; then
|
||||
# API is not available at this port/protocol combination
|
||||
API_PORT=""
|
||||
else
|
||||
# API is available at this URL combination
|
||||
|
||||
if [ "${availabilityResponse}" = 200 ]; then
|
||||
# API is available without authentication
|
||||
needAuth=false
|
||||
fi
|
||||
|
||||
if [ "${authStatus}" = 200 ]; then
|
||||
# API is available without authentication
|
||||
apiAvailable=true
|
||||
needAuth=false
|
||||
break
|
||||
fi
|
||||
|
||||
# Remove the first URL from the list
|
||||
local last_api_list
|
||||
last_api_list="${chaos_api_list}"
|
||||
chaos_api_list="${chaos_api_list#* }"
|
||||
elif [ "${authStatus}" = 401 ]; then
|
||||
# API is available with authentication
|
||||
apiAvailable=true
|
||||
needAuth=true
|
||||
# Check if 2FA is required
|
||||
needTOTP=$(echo "${authData}"| jq --raw-output .session.totp 2>/dev/null)
|
||||
break
|
||||
|
||||
# If the list did not change, we are at the last element
|
||||
if [ "${last_api_list}" = "${chaos_api_list}" ]; then
|
||||
# Remove the last element
|
||||
chaos_api_list=""
|
||||
else
|
||||
# API is not available at this port/protocol combination
|
||||
apiAvailable=false
|
||||
# Remove the first URL from the list
|
||||
local last_api_list
|
||||
last_api_list="${chaos_api_list}"
|
||||
chaos_api_list="${chaos_api_list#* }"
|
||||
|
||||
# If the list did not change, we are at the last element
|
||||
if [ "${last_api_list}" = "${chaos_api_list}" ]; then
|
||||
# Remove the last element
|
||||
chaos_api_list=""
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# if API_PORT is empty, no working API port was found
|
||||
if [ -n "${API_PORT}" ]; then
|
||||
echo "API not available at: ${API_URL}"
|
||||
# if apiAvailable is false, no working API was found
|
||||
if [ "${apiAvailable}" = false ]; then
|
||||
echo "API not available. Please check FTL.log"
|
||||
echo "Exiting."
|
||||
exit 1
|
||||
fi
|
||||
@@ -108,22 +125,58 @@ LoginAPI() {
|
||||
echo "API Authentication: Trying to use CLI password"
|
||||
fi
|
||||
|
||||
# Try to authenticate using the CLI password
|
||||
Authentication "${1}"
|
||||
|
||||
# If we can read the CLI password, we can skip 2FA even when it's required otherwise
|
||||
needTOTP=false
|
||||
elif [ "${1}" = "verbose" ]; then
|
||||
echo "API Authentication: CLI password not available"
|
||||
fi
|
||||
|
||||
if [ -z "${password}" ]; then
|
||||
# no password read from CLI file
|
||||
echo "Please enter your password:"
|
||||
# secretly read the password
|
||||
secretRead; printf '\n'
|
||||
fi
|
||||
|
||||
if [ "${needTOTP}" = true ]; then
|
||||
# 2FA required
|
||||
echo "Please enter the correct second factor."
|
||||
echo "(Can be any number if you used the app password)"
|
||||
read -r totp
|
||||
fi
|
||||
|
||||
# If this did not work, ask the user for the password
|
||||
while [ "${validSession}" = false ] || [ -z "${validSession}" ] ; do
|
||||
echo "Authentication failed. Please enter your Pi-hole password"
|
||||
# Try to authenticate using the supplied password (CLI file or user input) and TOTP
|
||||
Authentication "${1}"
|
||||
|
||||
# Try to login again until the session is valid
|
||||
while [ ! "${validSession}" = true ] ; do
|
||||
|
||||
# Print the error message if there is one
|
||||
if [ ! "${sessionError}" = "null" ] && [ "${1}" = "verbose" ]; then
|
||||
echo "Error: ${sessionError}"
|
||||
fi
|
||||
# Print the session message if there is one
|
||||
if [ ! "${sessionMessage}" = "null" ] && [ "${1}" = "verbose" ]; then
|
||||
echo "Error: ${sessionMessage}"
|
||||
fi
|
||||
|
||||
if [ "${1}" = "verbose" ]; then
|
||||
# If we are not in verbose mode, no need to print the error message again
|
||||
echo "Please enter your Pi-hole password"
|
||||
else
|
||||
|
||||
echo "Authentication failed. Please enter your Pi-hole password"
|
||||
fi
|
||||
|
||||
# secretly read the password
|
||||
secretRead; printf '\n'
|
||||
|
||||
if [ "${needTOTP}" = true ]; then
|
||||
echo "Please enter the correct second factor:"
|
||||
echo "(Can be any number if you used the app password)"
|
||||
read -r totp
|
||||
fi
|
||||
|
||||
# Try to authenticate again
|
||||
Authentication "${1}"
|
||||
done
|
||||
@@ -131,23 +184,34 @@ LoginAPI() {
|
||||
}
|
||||
|
||||
Authentication() {
|
||||
sessionResponse="$(curl -skS -X POST "${API_URL}auth" --user-agent "Pi-hole cli " --data "{\"password\":\"${password}\"}" )"
|
||||
sessionResponse="$(curl --connect-timeout 2 -skS -X POST "${API_URL}auth" --user-agent "Pi-hole cli" --data "{\"password\":\"${password}\", \"totp\":${totp:-null}}" )"
|
||||
|
||||
if [ -z "${sessionResponse}" ]; then
|
||||
echo "No response from FTL server. Please check connectivity"
|
||||
exit 1
|
||||
fi
|
||||
# obtain validity and session ID from session response
|
||||
validSession=$(echo "${sessionResponse}"| jq .session.valid 2>/dev/null)
|
||||
SID=$(echo "${sessionResponse}"| jq --raw-output .session.sid 2>/dev/null)
|
||||
|
||||
if [ "${1}" = "verbose" ]; then
|
||||
if [ "${validSession}" = true ]; then
|
||||
echo "API Authentication: ${COL_GREEN}Success${COL_NC}"
|
||||
else
|
||||
echo "API Authentication: ${COL_RED}Failed${COL_NC}"
|
||||
if [ -z "${sessionResponse}" ]; then
|
||||
echo "No response from FTL server. Please check connectivity"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# obtain validity, session ID, sessionMessage and error message from
|
||||
# session response, apply default values if none returned
|
||||
result=$(echo "${sessionResponse}" | jq -r '
|
||||
(.session.valid // false),
|
||||
(.session.sid // null),
|
||||
(.session.message // null),
|
||||
(.error.message // null)
|
||||
' 2>/dev/null)
|
||||
|
||||
validSession=$(echo "${result}" | sed -n '1p')
|
||||
SID=$(echo "${result}" | sed -n '2p')
|
||||
sessionMessage=$(echo "${result}" | sed -n '3p')
|
||||
sessionError=$(echo "${result}" | sed -n '4p')
|
||||
|
||||
if [ "${1}" = "verbose" ]; then
|
||||
if [ "${validSession}" = true ]; then
|
||||
echo "API Authentication: ${COL_GREEN}Success${COL_NC}"
|
||||
else
|
||||
echo "API Authentication: ${COL_RED}Failed${COL_NC}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
LogoutAPI() {
|
||||
@@ -185,7 +249,7 @@ GetFTLData() {
|
||||
# return only the data
|
||||
if [ "${status}" = 200 ]; then
|
||||
# response OK
|
||||
echo "${data}"
|
||||
printf %s "${data}"
|
||||
else
|
||||
# connection lost
|
||||
echo "${status}"
|
||||
@@ -259,14 +323,23 @@ secretRead() {
|
||||
}
|
||||
|
||||
apiFunc() {
|
||||
local data response status status_col
|
||||
local data response status status_col verbosity
|
||||
|
||||
# Define if the output will be silent (default) or verbose
|
||||
verbosity="silent"
|
||||
if [ "$1" = "verbose" ]; then
|
||||
verbosity="verbose"
|
||||
shift
|
||||
fi
|
||||
|
||||
# Authenticate with the API
|
||||
LoginAPI verbose
|
||||
echo ""
|
||||
LoginAPI "${verbosity}"
|
||||
|
||||
echo "Requesting: ${COL_PURPLE}GET ${COL_CYAN}${API_URL}${COL_YELLOW}$1${COL_NC}"
|
||||
echo ""
|
||||
if [ "${verbosity}" = "verbose" ]; then
|
||||
echo ""
|
||||
echo "Requesting: ${COL_PURPLE}GET ${COL_CYAN}${API_URL}${COL_YELLOW}$1${COL_NC}"
|
||||
echo ""
|
||||
fi
|
||||
|
||||
# Get the data from the API
|
||||
response=$(GetFTLData "$1" raw)
|
||||
@@ -283,17 +356,21 @@ apiFunc() {
|
||||
else
|
||||
status_col="${COL_RED}"
|
||||
fi
|
||||
echo "Status: ${status_col}${status}${COL_NC}"
|
||||
|
||||
# Only print the status in verbose mode or if the status is not 200
|
||||
if [ "${verbosity}" = "verbose" ] || [ "${status}" != 200 ]; then
|
||||
echo "Status: ${status_col}${status}${COL_NC}"
|
||||
fi
|
||||
|
||||
# Output the data. Format it with jq if available and data is actually JSON.
|
||||
# Otherwise just print it
|
||||
echo "Data:"
|
||||
if command -v jq >/dev/null && echo "${data}" | jq . >/dev/null 2>&1; then
|
||||
echo "${data}" | jq .
|
||||
else
|
||||
echo "${data}"
|
||||
if [ "${verbosity}" = "verbose" ]; then
|
||||
echo "Data:"
|
||||
fi
|
||||
# Attempt to print the data with jq, if it is not valid JSON, or not installed
|
||||
# then print the plain text.
|
||||
echo "${data}" | jq . 2>/dev/null || echo "${data}"
|
||||
|
||||
# Delete the session
|
||||
LogoutAPI verbose
|
||||
LogoutAPI "${verbosity}"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
|
||||
@@ -13,9 +13,8 @@
|
||||
readonly scriptPath="/etc/.pihole/advanced/Scripts/database_migration/gravity"
|
||||
|
||||
upgrade_gravityDB(){
|
||||
local database piholeDir version
|
||||
local database version
|
||||
database="${1}"
|
||||
piholeDir="${2}"
|
||||
|
||||
# Exit early if the database does not exist (e.g. in CI tests)
|
||||
if [[ ! -f "${database}" ]]; then
|
||||
@@ -151,4 +150,10 @@ upgrade_gravityDB(){
|
||||
pihole-FTL sqlite3 -ni "${database}" < "${scriptPath}/18_to_19.sql"
|
||||
version=19
|
||||
fi
|
||||
if [[ "$version" == "19" ]]; then
|
||||
# Update views to use new allowlist/denylist names
|
||||
echo -e " ${INFO} Upgrading gravity database from version 19 to 20"
|
||||
pihole-FTL sqlite3 -ni "${database}" < "${scriptPath}/19_to_20.sql"
|
||||
version=20
|
||||
fi
|
||||
}
|
||||
|
||||
43
advanced/Scripts/database_migration/gravity/19_to_20.sql
Normal file
43
advanced/Scripts/database_migration/gravity/19_to_20.sql
Normal file
@@ -0,0 +1,43 @@
|
||||
.timeout 30000
|
||||
|
||||
BEGIN TRANSACTION;
|
||||
|
||||
DROP VIEW vw_whitelist;
|
||||
CREATE VIEW vw_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
|
||||
AND domainlist.type = 0
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
DROP VIEW vw_blacklist;
|
||||
CREATE VIEW vw_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
|
||||
AND domainlist.type = 1
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
DROP VIEW vw_regex_whitelist;
|
||||
CREATE VIEW vw_regex_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
|
||||
AND domainlist.type = 2
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
DROP VIEW vw_regex_blacklist;
|
||||
CREATE VIEW vw_regex_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
|
||||
AND domainlist.type = 3
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
UPDATE info SET value = 20 WHERE property = 'version';
|
||||
|
||||
COMMIT;
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
@@ -10,11 +9,13 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
source "${utilsfile}"
|
||||
|
||||
readonly apifile="${PI_HOLE_SCRIPT_DIR}/api.sh"
|
||||
apifile="${PI_HOLE_SCRIPT_DIR}/api.sh"
|
||||
# shellcheck source="./advanced/Scripts/api.sh"
|
||||
source "${apifile}"
|
||||
|
||||
# Determine database location
|
||||
@@ -39,6 +40,7 @@ typeId=""
|
||||
comment=""
|
||||
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
# shellcheck source="./advanced/Scripts/COL_TABLE"
|
||||
source ${colfile}
|
||||
|
||||
helpFunc() {
|
||||
|
||||
@@ -1,82 +0,0 @@
|
||||
#!/usr/bin/env bash
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# ARP table interaction
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
if [[ -f ${coltable} ]]; then
|
||||
source ${coltable}
|
||||
fi
|
||||
|
||||
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
source "${utilsfile}"
|
||||
|
||||
# Determine database location
|
||||
DBFILE=$(getFTLConfigValue "files.database")
|
||||
if [ -z "$DBFILE" ]; then
|
||||
DBFILE="/etc/pihole/pihole-FTL.db"
|
||||
fi
|
||||
|
||||
flushARP(){
|
||||
local output
|
||||
if [[ "${args[1]}" != "quiet" ]]; then
|
||||
echo -ne " ${INFO} Flushing network table ..."
|
||||
fi
|
||||
|
||||
# Stop FTL to prevent database access
|
||||
if ! output=$(service pihole-FTL stop 2>&1); then
|
||||
echo -e "${OVER} ${CROSS} Failed to stop FTL"
|
||||
echo " Output: ${output}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Truncate network_addresses table in pihole-FTL.db
|
||||
# This needs to be done before we can truncate the network table due to
|
||||
# foreign key constraints
|
||||
if ! output=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM network_addresses" 2>&1); then
|
||||
echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table"
|
||||
echo " Database location: ${DBFILE}"
|
||||
echo " Output: ${output}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Truncate network table in pihole-FTL.db
|
||||
if ! output=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM network" 2>&1); then
|
||||
echo -e "${OVER} ${CROSS} Failed to truncate network table"
|
||||
echo " Database location: ${DBFILE}"
|
||||
echo " Output: ${output}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Flush ARP cache of the host
|
||||
if ! output=$(ip -s -s neigh flush all 2>&1); then
|
||||
echo -e "${OVER} ${CROSS} Failed to flush ARP cache"
|
||||
echo " Output: ${output}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Start FTL again
|
||||
if ! output=$(service pihole-FTL restart 2>&1); then
|
||||
echo -e "${OVER} ${CROSS} Failed to restart FTL"
|
||||
echo " Output: ${output}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [[ "${args[1]}" != "quiet" ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed network table"
|
||||
fi
|
||||
}
|
||||
|
||||
args=("$@")
|
||||
|
||||
case "${args[0]}" in
|
||||
"arpflush" ) flushARP;;
|
||||
esac
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
|
||||
SKIP_INSTALL="true"
|
||||
# shellcheck source="./automated install/basic-install.sh"
|
||||
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
|
||||
# webInterfaceGitUrl set in basic-install.sh
|
||||
@@ -25,7 +26,7 @@ source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
warning1() {
|
||||
echo " Please note that changing branches severely alters your Pi-hole subsystems"
|
||||
echo " Features that work on the master branch, may not on a development branch"
|
||||
echo -e " ${COL_LIGHT_RED}This feature is NOT supported unless a Pi-hole developer explicitly asks!${COL_NC}"
|
||||
echo -e " ${COL_RED}This feature is NOT supported unless a Pi-hole developer explicitly asks!${COL_NC}"
|
||||
read -r -p " Have you read and understood this? [y/N] " response
|
||||
case "${response}" in
|
||||
[yY][eE][sS]|[yY])
|
||||
@@ -40,6 +41,22 @@ warning1() {
|
||||
}
|
||||
|
||||
checkout() {
|
||||
|
||||
local skipFTL additionalFlag
|
||||
skipFTL=false
|
||||
# Check arguments
|
||||
for var in "$@"; do
|
||||
case "$var" in
|
||||
"--skipFTL") skipFTL=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ "${skipFTL}" == true ]; then
|
||||
additionalFlag="--skipFTL"
|
||||
else
|
||||
additionalFlag=""
|
||||
fi
|
||||
|
||||
local corebranches
|
||||
local webbranches
|
||||
|
||||
@@ -54,19 +71,19 @@ checkout() {
|
||||
|
||||
# This is unlikely
|
||||
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo -e " ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"
|
||||
echo -e " ${COL_RED}Error: Core Pi-hole repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if ! is_repo "${webInterfaceDir}" ; then
|
||||
echo -e " ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!"
|
||||
echo -e " ${COL_RED}Error: Web Admin repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
|
||||
if [[ -z "${1}" ]]; then
|
||||
echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}"
|
||||
echo -e " ${COL_RED}Invalid option${COL_NC}"
|
||||
echo -e " Try 'pihole checkout --help' for more information."
|
||||
exit 1
|
||||
fi
|
||||
@@ -109,7 +126,7 @@ checkout() {
|
||||
echo -e "${OVER} ${CROSS} $str"
|
||||
exit 1
|
||||
fi
|
||||
corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}"))
|
||||
mapfile -t corebranches < <(get_available_branches "${PI_HOLE_FILES_DIR}")
|
||||
|
||||
if [[ "${corebranches[*]}" == *"master"* ]]; then
|
||||
echo -e "${OVER} ${TICK} $str"
|
||||
@@ -136,7 +153,7 @@ checkout() {
|
||||
echo -e "${OVER} ${CROSS} $str"
|
||||
exit 1
|
||||
fi
|
||||
webbranches=($(get_available_branches "${webInterfaceDir}"))
|
||||
mapfile -t webbranches < <(get_available_branches "${webInterfaceDir}")
|
||||
|
||||
if [[ "${webbranches[*]}" == *"master"* ]]; then
|
||||
echo -e "${OVER} ${TICK} $str"
|
||||
@@ -167,7 +184,7 @@ checkout() {
|
||||
|
||||
# Check if requested branch is available
|
||||
echo -e " ${INFO} Checking for availability of branch ${COL_CYAN}${2}${COL_NC} on GitHub"
|
||||
ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep "refs/heads" | cut -d'/' -f3- -) )
|
||||
mapfile -t ftlbranches < <(git ls-remote https://github.com/pi-hole/ftl | grep "refs/heads" | cut -d'/' -f3- -)
|
||||
# If returned array is empty -> connectivity issue
|
||||
if [[ ${#ftlbranches[@]} -eq 0 ]]; then
|
||||
echo -e " ${CROSS} Unable to fetch branches from GitHub. Please check your Internet connection and try again later."
|
||||
@@ -209,13 +226,15 @@ checkout() {
|
||||
# Update local and remote versions via updatechecker
|
||||
/opt/pihole/updatecheck.sh
|
||||
else
|
||||
if [ $? -eq 1 ]; then
|
||||
local status
|
||||
status=$?
|
||||
if [ $status -eq 1 ]; then
|
||||
# Binary for requested branch is not available, may still be
|
||||
# int he process of being built or CI build job failed
|
||||
printf " %b Binary for requested branch is not available, please try again later.\\n" ${CROSS}
|
||||
printf " %b Binary for requested branch is not available, please try again later.\\n" "${CROSS}"
|
||||
printf " If the issue persists, please contact Pi-hole Support and ask them to re-generate the binary.\\n"
|
||||
exit 1
|
||||
elif [ $? -eq 2 ]; then
|
||||
elif [ $status -eq 2 ]; then
|
||||
printf " %b Unable to download from ftl.pi-hole.net. Please check your Internet connection and try again later.\\n" "${CROSS}"
|
||||
exit 1
|
||||
else
|
||||
@@ -232,10 +251,10 @@ checkout() {
|
||||
# Force updating everything
|
||||
if [[ ! "${1}" == "web" && ! "${1}" == "ftl" ]]; then
|
||||
echo -e " ${INFO} Running installer to upgrade your installation"
|
||||
if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then
|
||||
if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended ${additionalFlag}; then
|
||||
exit 0
|
||||
else
|
||||
echo -e " ${COL_LIGHT_RED} Error: Unable to complete update, please contact support${COL_NC}"
|
||||
echo -e " ${COL_RED} Error: Unable to complete update, please contact support${COL_NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
|
||||
# -e option instructs bash to immediately exit if any command [1] has a non-zero exit status
|
||||
# -u a reference to any variable you haven't previously defined
|
||||
@@ -27,6 +26,7 @@ PIHOLE_COLTABLE_FILE="${PIHOLE_SCRIPTS_DIRECTORY}/COL_TABLE"
|
||||
|
||||
# These provide the colors we need for making the log more readable
|
||||
if [[ -f ${PIHOLE_COLTABLE_FILE} ]]; then
|
||||
# shellcheck source=./advanced/Scripts/COL_TABLE
|
||||
source ${PIHOLE_COLTABLE_FILE}
|
||||
else
|
||||
COL_NC='\e[0m' # No Color
|
||||
@@ -41,7 +41,7 @@ else
|
||||
#OVER="\r\033[K"
|
||||
fi
|
||||
|
||||
# shellcheck disable=SC1091
|
||||
# shellcheck source=/dev/null
|
||||
. /etc/pihole/versions
|
||||
|
||||
# Read the value of an FTL config key. The value is printed to stdout.
|
||||
@@ -213,7 +213,7 @@ compare_local_version_to_git_version() {
|
||||
local local_status
|
||||
local_status=$(git status -s)
|
||||
# echo this information out to the user in a nice format
|
||||
if [ ${local_version} ]; then
|
||||
if [ "${local_version}" ]; then
|
||||
log_write "${TICK} Version: ${local_version}"
|
||||
elif [ -n "${DOCKER_VERSION}" ]; then
|
||||
log_write "${TICK} Version: Pi-hole Docker Container ${COL_BOLD}${DOCKER_VERSION}${COL_NC}"
|
||||
@@ -296,91 +296,12 @@ check_component_versions() {
|
||||
check_ftl_version
|
||||
}
|
||||
|
||||
os_check() {
|
||||
# This function gets a list of supported OS versions from a TXT record at versions.pi-hole.net
|
||||
# and determines whether or not the script is running on one of those systems
|
||||
local remote_os_domain valid_os valid_version detected_os detected_version cmdResult digReturnCode response
|
||||
remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"versions.pi-hole.net"}
|
||||
|
||||
detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"')
|
||||
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
|
||||
|
||||
cmdResult="$(dig -4 +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)"
|
||||
#Get the return code of the previous command (last line)
|
||||
digReturnCode="${cmdResult##*$'\n'}"
|
||||
|
||||
# Extract dig response
|
||||
response="${cmdResult%%$'\n'*}"
|
||||
|
||||
if [ "${digReturnCode}" -ne 0 ]; then
|
||||
log_write "${INFO} Distro: ${detected_os^}"
|
||||
log_write "${INFO} Version: ${detected_version}"
|
||||
log_write "${CROSS} dig IPv4 return code: ${COL_RED}${digReturnCode}${COL_NC}"
|
||||
log_write "${CROSS} dig response: ${response}"
|
||||
log_write "${INFO} Retrying via IPv6"
|
||||
|
||||
cmdResult="$(dig -6 +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)"
|
||||
#Get the return code of the previous command (last line)
|
||||
digReturnCode="${cmdResult##*$'\n'}"
|
||||
|
||||
# Extract dig response
|
||||
response="${cmdResult%%$'\n'*}"
|
||||
fi
|
||||
# If also no success via IPv6
|
||||
if [ "${digReturnCode}" -ne 0 ]; then
|
||||
log_write "${CROSS} dig IPv6 return code: ${COL_RED}${digReturnCode}${COL_NC}"
|
||||
log_write "${CROSS} dig response: ${response}"
|
||||
log_write "${CROSS} Error: ${COL_RED}dig command failed - Unable to check OS${COL_NC}"
|
||||
else
|
||||
IFS=" " read -r -a supportedOS < <(echo "${response}" | tr -d '"')
|
||||
for distro_and_versions in "${supportedOS[@]}"
|
||||
do
|
||||
distro_part="${distro_and_versions%%=*}"
|
||||
versions_part="${distro_and_versions##*=}"
|
||||
|
||||
if [[ "${detected_os^^}" =~ ${distro_part^^} ]]; then
|
||||
valid_os=true
|
||||
IFS="," read -r -a supportedVer <<<"${versions_part}"
|
||||
for version in "${supportedVer[@]}"
|
||||
do
|
||||
if [[ "${detected_version}" =~ $version ]]; then
|
||||
valid_version=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If it is a docker container, we can assume the OS is supported
|
||||
[ -n "${DOCKER_VERSION}" ] && valid_os=true && valid_version=true
|
||||
|
||||
local finalmsg
|
||||
if [ "$valid_os" = true ]; then
|
||||
log_write "${TICK} Distro: ${COL_GREEN}${detected_os^}${COL_NC}"
|
||||
|
||||
if [ "$valid_version" = true ]; then
|
||||
log_write "${TICK} Version: ${COL_GREEN}${detected_version}${COL_NC}"
|
||||
finalmsg="${TICK} ${COL_GREEN}Distro and version supported${COL_NC}"
|
||||
else
|
||||
log_write "${CROSS} Version: ${COL_RED}${detected_version}${COL_NC}"
|
||||
finalmsg="${CROSS} Error: ${COL_RED}${detected_os^} is supported but version ${detected_version} is currently unsupported ${COL_NC}(${FAQ_HARDWARE_REQUIREMENTS})${COL_NC}"
|
||||
fi
|
||||
else
|
||||
log_write "${CROSS} Distro: ${COL_RED}${detected_os^}${COL_NC}"
|
||||
finalmsg="${CROSS} Error: ${COL_RED}${detected_os^} is not a supported distro ${COL_NC}(${FAQ_HARDWARE_REQUIREMENTS})${COL_NC}"
|
||||
fi
|
||||
|
||||
# Print dig response and the final check result
|
||||
log_write "${TICK} dig return code: ${COL_GREEN}${digReturnCode}${COL_NC}"
|
||||
log_write "${INFO} dig response: ${response}"
|
||||
log_write "${finalmsg}"
|
||||
fi
|
||||
}
|
||||
|
||||
diagnose_operating_system() {
|
||||
# error message in a variable so we can easily modify it later (or reuse it)
|
||||
local error_msg="Distribution unknown -- most likely you are on an unsupported platform and may run into issues."
|
||||
local detected_os
|
||||
local detected_version
|
||||
|
||||
# Display the current test that is running
|
||||
echo_current_diagnostic "Operating system"
|
||||
|
||||
@@ -389,8 +310,13 @@ diagnose_operating_system() {
|
||||
|
||||
# If there is a /etc/*release file, it's probably a supported operating system, so we can
|
||||
if ls /etc/*release 1> /dev/null 2>&1; then
|
||||
# display the attributes to the user from the function made earlier
|
||||
os_check
|
||||
# display the attributes to the user
|
||||
|
||||
detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"')
|
||||
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
|
||||
|
||||
log_write "${INFO} Distro: ${detected_os^}"
|
||||
log_write "${INFO} Version: ${detected_version}"
|
||||
else
|
||||
# If it doesn't exist, it's not a system we currently support and link to FAQ
|
||||
log_write "${CROSS} ${COL_RED}${error_msg}${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS})"
|
||||
@@ -441,7 +367,7 @@ check_firewalld() {
|
||||
# test common required service ports
|
||||
local firewalld_enabled_services
|
||||
firewalld_enabled_services=$(firewall-cmd --list-services)
|
||||
local firewalld_expected_services=("http" "dns" "dhcp" "dhcpv6")
|
||||
local firewalld_expected_services=("http" "https" "dns" "dhcp" "dhcpv6" "ntp")
|
||||
for i in "${firewalld_expected_services[@]}"; do
|
||||
if [[ "${firewalld_enabled_services}" =~ ${i} ]]; then
|
||||
log_write "${TICK} ${COL_GREEN} Allow Service: ${i}${COL_NC}";
|
||||
@@ -449,30 +375,6 @@ check_firewalld() {
|
||||
log_write "${CROSS} ${COL_RED} Allow Service: ${i}${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
|
||||
fi
|
||||
done
|
||||
# check for custom FTL FirewallD zone
|
||||
local firewalld_zones
|
||||
firewalld_zones=$(firewall-cmd --get-zones)
|
||||
if [[ "${firewalld_zones}" =~ "ftl" ]]; then
|
||||
log_write "${TICK} ${COL_GREEN}FTL Custom Zone Detected${COL_NC}";
|
||||
# check FTL custom zone interface: lo
|
||||
local firewalld_ftl_zone_interfaces
|
||||
firewalld_ftl_zone_interfaces=$(firewall-cmd --zone=ftl --list-interfaces)
|
||||
if [[ "${firewalld_ftl_zone_interfaces}" =~ "lo" ]]; then
|
||||
log_write "${TICK} ${COL_GREEN} Local Interface Detected${COL_NC}";
|
||||
else
|
||||
log_write "${CROSS} ${COL_RED} Local Interface Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
|
||||
fi
|
||||
# check FTL custom zone port: 4711
|
||||
local firewalld_ftl_zone_ports
|
||||
firewalld_ftl_zone_ports=$(firewall-cmd --zone=ftl --list-ports)
|
||||
if [[ "${firewalld_ftl_zone_ports}" =~ "4711/tcp" ]]; then
|
||||
log_write "${TICK} ${COL_GREEN} FTL Port 4711/tcp Detected${COL_NC}";
|
||||
else
|
||||
log_write "${CROSS} ${COL_RED} FTL Port 4711/tcp Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
|
||||
fi
|
||||
else
|
||||
log_write "${CROSS} ${COL_RED}FTL Custom Zone Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
|
||||
fi
|
||||
fi
|
||||
else
|
||||
log_write "${TICK} ${COL_GREEN}Firewalld service not detected${COL_NC}";
|
||||
@@ -488,7 +390,9 @@ run_and_print_command() {
|
||||
local output
|
||||
output=$(${cmd} 2>&1)
|
||||
# If the command was successful,
|
||||
if [[ $? -eq 0 ]]; then
|
||||
local return_code
|
||||
return_code=$?
|
||||
if [[ "${return_code}" -eq 0 ]]; then
|
||||
# show the output
|
||||
log_write "${output}"
|
||||
else
|
||||
@@ -569,16 +473,25 @@ ping_gateway() {
|
||||
ping_ipv4_or_ipv6 "${protocol}"
|
||||
# Check if we are using IPv4 or IPv6
|
||||
# Find the default gateways using IPv4 or IPv6
|
||||
local gateway gateway_addr gateway_iface
|
||||
local gateway gateway_addr gateway_iface default_route
|
||||
|
||||
log_write "${INFO} Default IPv${protocol} gateway(s):"
|
||||
|
||||
while IFS= read -r gateway; do
|
||||
log_write " $(cut -d ' ' -f 3 <<< "${gateway}")%$(cut -d ' ' -f 5 <<< "${gateway}")"
|
||||
done < <(ip -"${protocol}" route | grep default)
|
||||
while IFS= read -r default_route; do
|
||||
gateway_addr=$(jq -r '.gateway' <<< "${default_route}")
|
||||
gateway_iface=$(jq -r '.dev' <<< "${default_route}")
|
||||
log_write " ${gateway_addr}%${gateway_iface}"
|
||||
done < <(ip -j -"${protocol}" route | jq -c '.[] | select(.dst == "default")')
|
||||
|
||||
# Find the first default route
|
||||
default_route=$(ip -j -"${protocol}" route show default)
|
||||
if echo "$default_route" | grep 'gateway' | grep -q 'dev'; then
|
||||
gateway_addr=$(echo "$default_route" | jq -r -c '.[0].gateway')
|
||||
gateway_iface=$(echo "$default_route" | jq -r -c '.[0].dev')
|
||||
else
|
||||
log_write " Unable to determine gateway address for IPv${protocol}"
|
||||
fi
|
||||
|
||||
gateway_addr=$(ip -"${protocol}" route | grep default | cut -d ' ' -f 3 | head -n 1)
|
||||
gateway_iface=$(ip -"${protocol}" route | grep default | cut -d ' ' -f 5 | head -n 1)
|
||||
# If there was at least one gateway
|
||||
if [ -n "${gateway_addr}" ]; then
|
||||
# Append the interface to the gateway address if it is a link-local address
|
||||
@@ -664,18 +577,21 @@ check_required_ports() {
|
||||
# Add port 53
|
||||
ports_configured+=("53")
|
||||
|
||||
local protocol_type port_number service_name
|
||||
# Now that we have the values stored,
|
||||
for i in "${!ports_in_use[@]}"; do
|
||||
# loop through them and assign some local variables
|
||||
local service_name
|
||||
service_name=$(echo "${ports_in_use[$i]}" | awk '{gsub(/users:\(\("/,"",$7);gsub(/".*/,"",$7);print $7}')
|
||||
local protocol_type
|
||||
protocol_type=$(echo "${ports_in_use[$i]}" | awk '{print $1}')
|
||||
local port_number
|
||||
port_number="$(echo "${ports_in_use[$i]}" | awk '{print $5}')" # | awk '{gsub(/^.*:/,"",$5);print $5}')
|
||||
read -r protocol_type port_number service_name <<< "$(
|
||||
awk '{
|
||||
p=$1; n=$5; s=$7
|
||||
gsub(/users:\(\("/,"",s)
|
||||
gsub(/".*/,"",s)
|
||||
print p, n, s
|
||||
}' <<< "${ports_in_use[$i]}"
|
||||
)"
|
||||
|
||||
# Check if the right services are using the right ports
|
||||
if [[ ${ports_configured[*]} =~ $(echo "${port_number}" | rev | cut -d: -f1 | rev) ]]; then
|
||||
if [[ ${ports_configured[*]} =~ ${port_number##*:} ]]; then
|
||||
compare_port_to_service_assigned "${ftl}" "${service_name}" "${protocol_type}:${port_number}"
|
||||
else
|
||||
# If it's not a default port that Pi-hole needs, just print it out for the user to see
|
||||
@@ -743,7 +659,7 @@ dig_at() {
|
||||
local record_type="A"
|
||||
fi
|
||||
|
||||
# Find a random blocked url that has not been whitelisted and is not ABP style.
|
||||
# Find a random blocked url that has not been allowlisted and is not ABP style.
|
||||
# This helps emulate queries to different domains that a user might query
|
||||
# It will also give extra assurance that Pi-hole is correctly resolving and blocking domains
|
||||
local random_url
|
||||
@@ -793,7 +709,7 @@ dig_at() {
|
||||
fi
|
||||
|
||||
# Check if Pi-hole can use itself to block a domain
|
||||
if local_dig="$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${local_address}" "${record_type}")"; then
|
||||
if local_dig="$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${local_address}" "${record_type}" -p "$(get_ftl_conf_value "dns.port")")"; then
|
||||
# If it can, show success
|
||||
if [[ "${local_dig}" == *"status: NOERROR"* ]]; then
|
||||
local_dig="NOERROR"
|
||||
@@ -849,7 +765,7 @@ process_status(){
|
||||
:
|
||||
else
|
||||
# non-Docker system
|
||||
if service "${i}" status | grep -E 'is\srunning' &> /dev/null; then
|
||||
if service "${i}" status | grep -q -E 'is\srunning|started'; then
|
||||
status_of_process="active"
|
||||
else
|
||||
status_of_process="inactive"
|
||||
@@ -887,42 +803,27 @@ ftl_full_status(){
|
||||
|
||||
make_array_from_file() {
|
||||
local filename="${1}"
|
||||
|
||||
# If the file is a directory do nothing since it cannot be parsed
|
||||
[[ -d "${filename}" ]] && return
|
||||
|
||||
# The second argument can put a limit on how many line should be read from the file
|
||||
# Since some of the files are so large, this is helpful to limit the output
|
||||
local limit=${2}
|
||||
# A local iterator for testing if we are at the limit above
|
||||
local i=0
|
||||
# If the file is a directory
|
||||
if [[ -d "${filename}" ]]; then
|
||||
# do nothing since it cannot be parsed
|
||||
:
|
||||
else
|
||||
# Otherwise, read the file line by line
|
||||
while IFS= read -r line;do
|
||||
# Otherwise, strip out comments and blank lines
|
||||
new_line=$(echo "${line}" | sed -e 's/^\s*#.*$//' -e '/^$/d')
|
||||
# If the line still has content (a non-zero value)
|
||||
if [[ -n "${new_line}" ]]; then
|
||||
|
||||
# If the string contains "### CHANGED", highlight this part in red
|
||||
if [[ "${new_line}" == *"### CHANGED"* ]]; then
|
||||
new_line="${new_line//### CHANGED/${COL_RED}### CHANGED${COL_NC}}"
|
||||
fi
|
||||
# Process the file, strip out comments and blank lines
|
||||
local processed
|
||||
processed=$(sed -e 's/^\s*#.*$//' -e '/^$/d' "${filename}")
|
||||
|
||||
# Finally, write this line to the log
|
||||
log_write " ${new_line}"
|
||||
fi
|
||||
# Increment the iterator +1
|
||||
i=$((i+1))
|
||||
# but if the limit of lines we want to see is exceeded
|
||||
if [[ -z ${limit} ]]; then
|
||||
# do nothing
|
||||
:
|
||||
elif [[ $i -eq ${limit} ]]; then
|
||||
break
|
||||
fi
|
||||
done < "${filename}"
|
||||
fi
|
||||
while IFS= read -r line; do
|
||||
# If the string contains "### CHANGED", highlight this part in red
|
||||
log_write " ${line//### CHANGED/${COL_RED}### CHANGED${COL_NC}}"
|
||||
((i++))
|
||||
# if the limit of lines we want to see is exceeded do nothing
|
||||
[[ -n ${limit} && $i -eq ${limit} ]] && break
|
||||
done <<< "$processed"
|
||||
}
|
||||
|
||||
parse_file() {
|
||||
@@ -933,7 +834,6 @@ parse_file() {
|
||||
# Get the lines that are in the file(s) and store them in an array for parsing later
|
||||
local file_info
|
||||
if [[ -f "$filename" ]]; then
|
||||
#shellcheck disable=SC2016
|
||||
IFS=$'\r\n' command eval 'file_info=( $(cat "${filename}") )'
|
||||
else
|
||||
read -r -a file_info <<< "$filename"
|
||||
@@ -996,38 +896,38 @@ list_files_in_dir() {
|
||||
fi
|
||||
|
||||
# Store the files found in an array
|
||||
mapfile -t files_found < <(ls "${dir_to_parse}")
|
||||
local files_found=("${dir_to_parse}"/*)
|
||||
# For each file in the array,
|
||||
for each_file in "${files_found[@]}"; do
|
||||
if [[ -d "${dir_to_parse}/${each_file}" ]]; then
|
||||
if [[ -d "${each_file}" ]]; then
|
||||
# If it's a directory, do nothing
|
||||
:
|
||||
elif [[ "${dir_to_parse}/${each_file}" == "${PIHOLE_DEBUG_LOG}" ]] || \
|
||||
[[ "${dir_to_parse}/${each_file}" == "${PIHOLE_RAW_BLOCKLIST_FILES}" ]] || \
|
||||
[[ "${dir_to_parse}/${each_file}" == "${PIHOLE_INSTALL_LOG_FILE}" ]] || \
|
||||
[[ "${dir_to_parse}/${each_file}" == "${PIHOLE_LOG}" ]] || \
|
||||
[[ "${dir_to_parse}/${each_file}" == "${PIHOLE_LOG_GZIPS}" ]]; then
|
||||
elif [[ "${each_file}" == "${PIHOLE_DEBUG_LOG}" ]] || \
|
||||
[[ "${each_file}" == "${PIHOLE_RAW_BLOCKLIST_FILES}" ]] || \
|
||||
[[ "${each_file}" == "${PIHOLE_INSTALL_LOG_FILE}" ]] || \
|
||||
[[ "${each_file}" == "${PIHOLE_LOG}" ]] || \
|
||||
[[ "${each_file}" == "${PIHOLE_LOG_GZIPS}" ]]; then
|
||||
:
|
||||
elif [[ "${dir_to_parse}" == "${DNSMASQ_D_DIRECTORY}" ]]; then
|
||||
# in case of the dnsmasq directory include all files in the debug output
|
||||
log_write "\\n${COL_GREEN}$(ls -lhd "${dir_to_parse}"/"${each_file}")${COL_NC}"
|
||||
make_array_from_file "${dir_to_parse}/${each_file}"
|
||||
log_write "\\n${COL_GREEN}$(ls -lhd "${each_file}")${COL_NC}"
|
||||
make_array_from_file "${each_file}"
|
||||
else
|
||||
# Then, parse the file's content into an array so each line can be analyzed if need be
|
||||
for i in "${!REQUIRED_FILES[@]}"; do
|
||||
if [[ "${dir_to_parse}/${each_file}" == "${REQUIRED_FILES[$i]}" ]]; then
|
||||
if [[ "${each_file}" == "${REQUIRED_FILES[$i]}" ]]; then
|
||||
# display the filename
|
||||
log_write "\\n${COL_GREEN}$(ls -lhd "${dir_to_parse}"/"${each_file}")${COL_NC}"
|
||||
log_write "\\n${COL_GREEN}$(ls -lhd "${each_file}")${COL_NC}"
|
||||
# Check if the file we want to view has a limit (because sometimes we just need a little bit of info from the file, not the entire thing)
|
||||
case "${dir_to_parse}/${each_file}" in
|
||||
case "${each_file}" in
|
||||
# If it's Web server log, give the first and last 25 lines
|
||||
"${PIHOLE_WEBSERVER_LOG}") head_tail_log "${dir_to_parse}/${each_file}" 25
|
||||
"${PIHOLE_WEBSERVER_LOG}") head_tail_log "${each_file}" 25
|
||||
;;
|
||||
# Same for the FTL log
|
||||
"${PIHOLE_FTL_LOG}") head_tail_log "${dir_to_parse}/${each_file}" 35
|
||||
"${PIHOLE_FTL_LOG}") head_tail_log "${each_file}" 35
|
||||
;;
|
||||
# parse the file into an array in case we ever need to analyze it line-by-line
|
||||
*) make_array_from_file "${dir_to_parse}/${each_file}";
|
||||
*) make_array_from_file "${each_file}";
|
||||
esac
|
||||
else
|
||||
# Otherwise, do nothing since it's not a file needed for Pi-hole so we don't care about it
|
||||
@@ -1063,6 +963,7 @@ head_tail_log() {
|
||||
local filename="${1}"
|
||||
# The number of lines to use for head and tail
|
||||
local qty="${2}"
|
||||
local filebasename="${filename##*/}"
|
||||
local head_line
|
||||
local tail_line
|
||||
# Put the current Internal Field Separator into another variable so it can be restored later
|
||||
@@ -1071,14 +972,14 @@ head_tail_log() {
|
||||
IFS=$'\r\n'
|
||||
local log_head=()
|
||||
mapfile -t log_head < <(head -n "${qty}" "${filename}")
|
||||
log_write " ${COL_CYAN}-----head of $(basename "${filename}")------${COL_NC}"
|
||||
log_write " ${COL_CYAN}-----head of ${filebasename}------${COL_NC}"
|
||||
for head_line in "${log_head[@]}"; do
|
||||
log_write " ${head_line}"
|
||||
done
|
||||
log_write ""
|
||||
local log_tail=()
|
||||
mapfile -t log_tail < <(tail -n "${qty}" "${filename}")
|
||||
log_write " ${COL_CYAN}-----tail of $(basename "${filename}")------${COL_NC}"
|
||||
log_write " ${COL_CYAN}-----tail of ${filebasename}------${COL_NC}"
|
||||
for tail_line in "${log_tail[@]}"; do
|
||||
log_write " ${tail_line}"
|
||||
done
|
||||
@@ -1105,6 +1006,24 @@ show_db_entries() {
|
||||
)
|
||||
|
||||
for line in "${entries[@]}"; do
|
||||
# Use gray color for "no". Normal color for "yes"
|
||||
line=${line//--no---/${COL_GRAY} no ${COL_NC}}
|
||||
line=${line//--yes--/ yes }
|
||||
|
||||
# Use red for "deny" and green for "allow"
|
||||
if [ "$title" = "Domainlist" ]; then
|
||||
line=${line//regex-deny/${COL_RED}regex-deny${COL_NC}}
|
||||
line=${line//regex-allow/${COL_GREEN}regex-allow${COL_NC}}
|
||||
line=${line//exact-deny/${COL_RED}exact-deny${COL_NC}}
|
||||
line=${line//exact-allow/${COL_GREEN}exact-allow${COL_NC}}
|
||||
fi
|
||||
|
||||
# Use red for "block" and green for "allow"
|
||||
if [ "$title" = "Adlists" ]; then
|
||||
line=${line//-BLOCK-/${COL_RED} Block ${COL_NC}}
|
||||
line=${line//-ALLOW-/${COL_GREEN} Allow ${COL_NC}}
|
||||
fi
|
||||
|
||||
log_write " ${line}"
|
||||
done
|
||||
|
||||
@@ -1152,15 +1071,15 @@ check_dhcp_servers() {
|
||||
}
|
||||
|
||||
show_groups() {
|
||||
show_db_entries "Groups" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,name,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,description FROM \"group\"" "4 7 50 19 19 50"
|
||||
show_db_entries "Groups" "SELECT id,CASE enabled WHEN '0' THEN '--no---' WHEN '1' THEN '--yes--' ELSE enabled END enabled,name,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,description FROM \"group\"" "4 7 50 19 19 50"
|
||||
}
|
||||
|
||||
show_adlists() {
|
||||
show_db_entries "Adlists" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids,address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;" "5 7 12 100 19 19 50"
|
||||
show_db_entries "Adlists" "SELECT id,CASE enabled WHEN '0' THEN '--no---' WHEN '1' THEN '--yes--' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids, CASE type WHEN '0' THEN '-BLOCK-' WHEN '1' THEN '-ALLOW-' ELSE type END type, address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;" "5 7 12 7 100 19 19 50"
|
||||
}
|
||||
|
||||
show_domainlist() {
|
||||
show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "5 4 7 12 100 19 19 50"
|
||||
show_db_entries "Domainlist" "SELECT id,CASE type WHEN '0' THEN 'exact-allow' WHEN '1' THEN 'exact-deny' WHEN '2' THEN 'regex-allow' WHEN '3' THEN 'regex-deny' ELSE type END type,CASE enabled WHEN '0' THEN '--no---' WHEN '1' THEN '--yes--' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "5 11 7 12 90 19 19 50"
|
||||
}
|
||||
|
||||
show_clients() {
|
||||
|
||||
@@ -9,17 +9,14 @@
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
# shellcheck source="./advanced/Scripts/COL_TABLE"
|
||||
source ${colfile}
|
||||
|
||||
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
source "${utilsfile}"
|
||||
|
||||
# In case we're running at the same time as a system logrotate, use a
|
||||
# separate logrotate state file to prevent stepping on each other's
|
||||
# toes.
|
||||
STATEFILE="/var/lib/logrotate/pihole"
|
||||
|
||||
# Determine database location
|
||||
DBFILE=$(getFTLConfigValue "files.database")
|
||||
if [ -z "$DBFILE" ]; then
|
||||
@@ -35,88 +32,45 @@ FTLFILE=$(getFTLConfigValue "files.log.ftl")
|
||||
if [ -z "$FTLFILE" ]; then
|
||||
FTLFILE="/var/log/pihole/FTL.log"
|
||||
fi
|
||||
|
||||
if [[ "$*" == *"once"* ]]; then
|
||||
# Nightly logrotation
|
||||
if command -v /usr/sbin/logrotate >/dev/null; then
|
||||
# Logrotate once
|
||||
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Running logrotate ..."
|
||||
fi
|
||||
/usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
|
||||
else
|
||||
# Copy pihole.log over to pihole.log.1
|
||||
# and empty out pihole.log
|
||||
# Note that moving the file is not an option, as
|
||||
# dnsmasq would happily continue writing into the
|
||||
# moved file (it will have the same file handler)
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Rotating ${LOGFILE} ..."
|
||||
fi
|
||||
cp -p "${LOGFILE}" "${LOGFILE}.1"
|
||||
echo " " > "${LOGFILE}"
|
||||
chmod 640 "${LOGFILE}"
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Rotated ${LOGFILE} ..."
|
||||
fi
|
||||
# Copy FTL.log over to FTL.log.1
|
||||
# and empty out FTL.log
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Rotating ${FTLFILE} ..."
|
||||
fi
|
||||
cp -p "${FTLFILE}" "${FTLFILE}.1"
|
||||
echo " " > "${FTLFILE}"
|
||||
chmod 640 "${FTLFILE}"
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Rotated ${FTLFILE} ..."
|
||||
fi
|
||||
fi
|
||||
else
|
||||
# Manual flushing
|
||||
|
||||
# Flush both pihole.log and pihole.log.1 (if existing)
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Flushing ${LOGFILE} ..."
|
||||
fi
|
||||
echo " " > "${LOGFILE}"
|
||||
chmod 640 "${LOGFILE}"
|
||||
if [ -f "${LOGFILE}.1" ]; then
|
||||
echo " " > "${LOGFILE}.1"
|
||||
chmod 640 "${LOGFILE}.1"
|
||||
fi
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed ${LOGFILE} ..."
|
||||
fi
|
||||
|
||||
# Flush both FTL.log and FTL.log.1 (if existing)
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Flushing ${FTLFILE} ..."
|
||||
fi
|
||||
echo " " > "${FTLFILE}"
|
||||
chmod 640 "${FTLFILE}"
|
||||
if [ -f "${FTLFILE}.1" ]; then
|
||||
echo " " > "${FTLFILE}.1"
|
||||
chmod 640 "${FTLFILE}.1"
|
||||
fi
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed ${FTLFILE} ..."
|
||||
fi
|
||||
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Flushing database, DNS resolution temporarily unavailable ..."
|
||||
fi
|
||||
|
||||
# Stop FTL to make sure it doesn't write to the database while we're deleting data
|
||||
service pihole-FTL stop
|
||||
|
||||
# Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history)
|
||||
deleted=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM query_storage WHERE timestamp >= strftime('%s','now')-86400; select changes() from query_storage limit 1")
|
||||
|
||||
# Restart FTL
|
||||
service pihole-FTL restart
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Deleted ${deleted} queries from long-term query database"
|
||||
fi
|
||||
WEBFILE=$(getFTLConfigValue "files.log.webserver")
|
||||
if [ -z "$WEBFILE" ]; then
|
||||
WEBFILE="/var/log/pihole/webserver.log"
|
||||
fi
|
||||
|
||||
# Helper function to handle log flushing for a single file
|
||||
flush_log() {
|
||||
local logfile="$1"
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Flushing ${logfile} ..."
|
||||
fi
|
||||
echo " " > "${logfile}"
|
||||
chmod 640 "${logfile}"
|
||||
if [ -f "${logfile}.1" ]; then
|
||||
echo " " > "${logfile}.1"
|
||||
chmod 640 "${logfile}.1"
|
||||
fi
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed ${logfile} ..."
|
||||
fi
|
||||
}
|
||||
|
||||
# Manual flushing
|
||||
flush_log "${LOGFILE}"
|
||||
flush_log "${FTLFILE}"
|
||||
flush_log "${WEBFILE}"
|
||||
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Flushing database, DNS resolution temporarily unavailable ..."
|
||||
fi
|
||||
|
||||
# Stop FTL to make sure it doesn't write to the database while we're deleting data
|
||||
service pihole-FTL stop
|
||||
|
||||
# Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history)
|
||||
deleted=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM query_storage WHERE timestamp >= strftime('%s','now')-86400; select changes() from query_storage limit 1")
|
||||
|
||||
# Restart FTL
|
||||
service pihole-FTL restart
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Deleted ${deleted} queries from long-term query database"
|
||||
fi
|
||||
|
||||
72
advanced/Scripts/piholeLogRotate.sh
Executable file
72
advanced/Scripts/piholeLogRotate.sh
Executable file
@@ -0,0 +1,72 @@
|
||||
#!/usr/bin/env bash
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2025 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Rotate Pi-hole's log file
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
# shellcheck source="./advanced/Scripts/COL_TABLE"
|
||||
source ${colfile}
|
||||
|
||||
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
source "${utilsfile}"
|
||||
|
||||
# In case we're running at the same time as a system logrotate, use a
|
||||
# separate logrotate state file to prevent stepping on each other's
|
||||
# toes.
|
||||
STATEFILE="/var/lib/logrotate/pihole"
|
||||
|
||||
|
||||
# Determine log file location
|
||||
LOGFILE=$(getFTLConfigValue "files.log.dnsmasq")
|
||||
if [ -z "$LOGFILE" ]; then
|
||||
LOGFILE="/var/log/pihole/pihole.log"
|
||||
fi
|
||||
FTLFILE=$(getFTLConfigValue "files.log.ftl")
|
||||
if [ -z "$FTLFILE" ]; then
|
||||
FTLFILE="/var/log/pihole/FTL.log"
|
||||
fi
|
||||
WEBFILE=$(getFTLConfigValue "files.log.webserver")
|
||||
if [ -z "$WEBFILE" ]; then
|
||||
WEBFILE="/var/log/pihole/webserver.log"
|
||||
fi
|
||||
|
||||
# Helper function to handle log rotation for a single file
|
||||
rotate_log() {
|
||||
# This function copies x.log over to x.log.1
|
||||
# and then empties x.log
|
||||
# Note that moving the file is not an option, as
|
||||
# dnsmasq would happily continue writing into the
|
||||
# moved file (it will have the same file handler)
|
||||
local logfile="$1"
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Rotating ${logfile} ..."
|
||||
fi
|
||||
cp -p "${logfile}" "${logfile}.1"
|
||||
echo " " > "${logfile}"
|
||||
chmod 640 "${logfile}"
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -e "${OVER} ${TICK} Rotated ${logfile} ..."
|
||||
fi
|
||||
}
|
||||
|
||||
# Nightly logrotation
|
||||
if command -v /usr/sbin/logrotate >/dev/null; then
|
||||
# Logrotate once
|
||||
if [[ "$*" != *"quiet"* ]]; then
|
||||
echo -ne " ${INFO} Running logrotate ..."
|
||||
fi
|
||||
mkdir -p "${STATEFILE%/*}"
|
||||
/usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
|
||||
else
|
||||
# Handle rotation for each log file
|
||||
rotate_log "${LOGFILE}"
|
||||
rotate_log "${FTLFILE}"
|
||||
rotate_log "${WEBFILE}"
|
||||
fi
|
||||
84
advanced/Scripts/piholeNetworkFlush.sh
Executable file
84
advanced/Scripts/piholeNetworkFlush.sh
Executable file
@@ -0,0 +1,84 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
|
||||
# Network-wide ad blocking via your own hardware.
|
||||
#
|
||||
# Network table flush
|
||||
#
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
coltable="/opt/pihole/COL_TABLE"
|
||||
if [[ -f ${coltable} ]]; then
|
||||
# shellcheck source="./advanced/Scripts/COL_TABLE"
|
||||
source ${coltable}
|
||||
fi
|
||||
|
||||
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck source=./advanced/Scripts/utils.sh
|
||||
source "${utilsfile}"
|
||||
|
||||
# Source api functions
|
||||
# shellcheck source="./advanced/Scripts/api.sh"
|
||||
. "${PI_HOLE_SCRIPT_DIR}/api.sh"
|
||||
|
||||
flushNetwork(){
|
||||
local output
|
||||
|
||||
echo -ne " ${INFO} Flushing network table ..."
|
||||
|
||||
local data status error
|
||||
# Authenticate with FTL
|
||||
LoginAPI
|
||||
|
||||
# send query again
|
||||
data=$(PostFTLData "action/flush/network" "" "status")
|
||||
|
||||
# Separate the status from the data
|
||||
status=$(printf %s "${data#"${data%???}"}")
|
||||
data=$(printf %s "${data%???}")
|
||||
|
||||
# If there is an .error object in the returned data, display it
|
||||
local error
|
||||
error=$(jq --compact-output <<< "${data}" '.error')
|
||||
if [[ $error != "null" && $error != "" ]]; then
|
||||
echo -e "${OVER} ${CROSS} Failed to flush the network table:"
|
||||
echo -e " $(jq <<< "${data}" '.error')"
|
||||
LogoutAPI
|
||||
exit 1
|
||||
elif [[ "${status}" == "200" ]]; then
|
||||
echo -e "${OVER} ${TICK} Flushed network table"
|
||||
fi
|
||||
|
||||
# Delete session
|
||||
LogoutAPI
|
||||
}
|
||||
|
||||
flushArp(){
|
||||
# Flush ARP cache of the host
|
||||
if ! output=$(ip -s -s neigh flush all 2>&1); then
|
||||
echo -e "${OVER} ${CROSS} Failed to flush ARP cache"
|
||||
echo " Output: ${output}"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Process all options (if present)
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
"--arp" ) doARP=true ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
flushNetwork
|
||||
|
||||
if [[ "${doARP}" == true ]]; then
|
||||
echo -ne " ${INFO} Flushing ARP cache"
|
||||
if flushArp; then
|
||||
echo -e "${OVER} ${TICK} Flushed ARP cache"
|
||||
fi
|
||||
fi
|
||||
|
||||
@@ -1,9 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC1090
|
||||
|
||||
# Ignore warning about `local` being undefinded in POSIX
|
||||
# shellcheck disable=SC3043
|
||||
# https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2023 Pi-hole, LLC (https://pi-hole.net)
|
||||
@@ -22,9 +17,11 @@ domain=""
|
||||
|
||||
# Source color table
|
||||
colfile="/opt/pihole/COL_TABLE"
|
||||
# shellcheck source="./advanced/Scripts/COL_TABLE"
|
||||
. "${colfile}"
|
||||
|
||||
# Source api functions
|
||||
# shellcheck source="./advanced/Scripts/api.sh"
|
||||
. "${PI_HOLE_INSTALL_DIR}/api.sh"
|
||||
|
||||
Help() {
|
||||
@@ -40,19 +37,16 @@ Options:
|
||||
}
|
||||
|
||||
GenerateOutput() {
|
||||
local data gravity_data lists_data num_gravity num_lists search_type_str
|
||||
local gravity_data_csv lists_data_csv line current_domain url type color
|
||||
local counts data num_gravity num_lists search_type_str
|
||||
local gravity_data_csv lists_data_csv line url type color
|
||||
data="${1}"
|
||||
|
||||
# construct a new json for the list results where each object contains the domain and the related type
|
||||
lists_data=$(printf %s "${data}" | jq '.search.domains | [.[] | {domain: .domain, type: .type}]')
|
||||
|
||||
# construct a new json for the gravity results where each object contains the adlist URL and the related domains
|
||||
gravity_data=$(printf %s "${data}" | jq '.search.gravity | group_by(.address,.type) | map({ address: (.[0].address), type: (.[0].type), domains: [.[] | .domain] })')
|
||||
|
||||
# number of objects in each json
|
||||
num_gravity=$(printf %s "${gravity_data}" | jq length)
|
||||
num_lists=$(printf %s "${lists_data}" | jq length)
|
||||
# Get count of list and gravity matches
|
||||
# Use JQ to count number of entries in lists and gravity
|
||||
# (output is number of list matches then number of gravity matches)
|
||||
counts=$(printf %s "${data}" | jq --raw-output '(.search.domains | length), (.search.gravity | group_by(.address,.type) | length)')
|
||||
num_lists=$(echo "$counts" | sed -n '1p')
|
||||
num_gravity=$(echo "$counts" | sed -n '2p')
|
||||
|
||||
if [ "${partial}" = true ]; then
|
||||
search_type_str="partially"
|
||||
@@ -65,7 +59,7 @@ GenerateOutput() {
|
||||
if [ "${num_lists}" -gt 0 ]; then
|
||||
# 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
|
||||
lists_data_csv=$(printf %s "${lists_data}" | jq --raw-output '.[] | [.domain, .type] | join(",")')
|
||||
lists_data_csv=$(printf %s "${data}" | jq --raw-output '.search.domains | map([.domain, .type] | join(",")) | join("\n")')
|
||||
|
||||
# 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
|
||||
@@ -74,11 +68,11 @@ GenerateOutput() {
|
||||
fi
|
||||
|
||||
# Results from gravity
|
||||
printf "%s\n\n" "Found ${num_gravity} adlists ${search_type_str} matching '${COL_BLUE}${domain}${COL_NC}'."
|
||||
printf "%s\n\n" "Found ${num_gravity} lists ${search_type_str} matching '${COL_BLUE}${domain}${COL_NC}'."
|
||||
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,type,domain,domain,...." string
|
||||
# not using jq's @csv here as it quotes each value individually
|
||||
gravity_data_csv=$(printf %s "${gravity_data}" | jq --raw-output '.[] | [.address, .type, .domains[]] | join(",")')
|
||||
gravity_data_csv=$(printf %s "${data}" | jq --raw-output '.search.gravity | group_by(.address,.type) | map([.[0].address, .[0].type, (.[] | .domain)] | join(",")) | join("\n")')
|
||||
|
||||
# Generate line-by-line output for each csv line
|
||||
echo "${gravity_data_csv}" | while read -r line; do
|
||||
@@ -100,15 +94,8 @@ GenerateOutput() {
|
||||
|
||||
# cut off type, leaving "domain,domain,...."
|
||||
line=${line#*,}
|
||||
# print each domain and remove it from the string until nothing is left
|
||||
while [ ${#line} -gt 0 ]; do
|
||||
current_domain=${line%%,*}
|
||||
printf ' - %s\n' "${COL_GREEN}${current_domain}${COL_NC}"
|
||||
# we need to remove the current_domain and the comma in two steps because
|
||||
# the last domain won't have a trailing comma and the while loop wouldn't exit
|
||||
line=${line#"${current_domain}"}
|
||||
line=${line#,}
|
||||
done
|
||||
# Replace commas with newlines and format output
|
||||
echo "${line}" | sed 's/,/\n/g' | sed "s/^/ - ${COL_GREEN}/" | sed "s/$/${COL_NC}/"
|
||||
printf "\n\n"
|
||||
done
|
||||
fi
|
||||
|
||||
@@ -12,26 +12,31 @@
|
||||
|
||||
# Variables
|
||||
readonly ADMIN_INTERFACE_GIT_URL="https://github.com/pi-hole/web.git"
|
||||
readonly ADMIN_INTERFACE_DIR="/var/www/html/admin"
|
||||
readonly PI_HOLE_GIT_URL="https://github.com/pi-hole/pi-hole.git"
|
||||
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
SKIP_INSTALL=true
|
||||
|
||||
# when --check-only is passed to this script, it will not perform the actual update
|
||||
CHECK_ONLY=false
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
# shellcheck source="./automated install/basic-install.sh"
|
||||
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
|
||||
# shellcheck disable=SC1091
|
||||
# shellcheck source=./advanced/Scripts/COL_TABLE
|
||||
source "/opt/pihole/COL_TABLE"
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
source "${PI_HOLE_INSTALL_DIR}/utils.sh"
|
||||
|
||||
# is_repo() sourced from basic-install.sh
|
||||
# make_repo() sourced from basic-install.sh
|
||||
# update_repo() source from basic-install.sh
|
||||
# getGitFiles() sourced from basic-install.sh
|
||||
# FTLcheckUpdate() sourced from basic-install.sh
|
||||
# getFTLConfigValue() sourced from utils.sh
|
||||
|
||||
# Honour configured paths for the web application.
|
||||
ADMIN_INTERFACE_DIR=$(getFTLConfigValue "webserver.paths.webroot")$(getFTLConfigValue "webserver.paths.webhome")
|
||||
readonly ADMIN_INTERFACE_DIR
|
||||
|
||||
GitCheckUpdateAvail() {
|
||||
local directory
|
||||
@@ -42,7 +47,7 @@ GitCheckUpdateAvail() {
|
||||
|
||||
# Fetch latest changes in this repo
|
||||
if ! git fetch --quiet origin ; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Unable to update local repository. Contact Pi-hole Support.${COL_NC}"
|
||||
echo -e "\\n ${COL_RED}Error: Unable to update local repository. Contact Pi-hole Support.${COL_NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
@@ -71,13 +76,13 @@ GitCheckUpdateAvail() {
|
||||
|
||||
|
||||
if [[ "${#LOCAL}" == 0 ]]; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support"
|
||||
echo -e "\\n ${COL_RED}Error: Local revision could not be obtained, please contact Pi-hole Support"
|
||||
echo -e " Additional debugging output:${COL_NC}"
|
||||
git status
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${#REMOTE}" == 0 ]]; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support"
|
||||
echo -e "\\n ${COL_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support"
|
||||
echo -e " Additional debugging output:${COL_NC}"
|
||||
git status
|
||||
exit 1
|
||||
@@ -142,7 +147,7 @@ Please confirm you want to start the update process." \
|
||||
}
|
||||
|
||||
main() {
|
||||
local basicError="\\n ${COL_LIGHT_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}"
|
||||
local basicError="\\n ${COL_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}"
|
||||
local core_update
|
||||
local web_update
|
||||
local FTL_update
|
||||
@@ -151,8 +156,6 @@ main() {
|
||||
web_update=false
|
||||
FTL_update=false
|
||||
|
||||
# Perform an OS check to ensure we're on an appropriate operating system
|
||||
os_check
|
||||
|
||||
# Install packages used by this installation script (necessary if users have removed e.g. git from their systems)
|
||||
package_manager_detect
|
||||
@@ -161,7 +164,7 @@ main() {
|
||||
|
||||
# This is unlikely
|
||||
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"
|
||||
echo -e "\\n ${COL_RED}Error: Core Pi-hole repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://pi-hole.net${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
@@ -173,11 +176,11 @@ main() {
|
||||
echo -e " ${INFO} Pi-hole Core:\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
core_update=false
|
||||
echo -e " ${INFO} Pi-hole Core:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
echo -e " ${INFO} Pi-hole Core:\\t${COL_GREEN}up to date${COL_NC}"
|
||||
fi
|
||||
|
||||
if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then
|
||||
echo -e "\\n ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!"
|
||||
echo -e "\\n ${COL_RED}Error: Web Admin repo is missing from system!"
|
||||
echo -e " Please re-run install script from https://pi-hole.net${COL_NC}"
|
||||
exit 1;
|
||||
fi
|
||||
@@ -187,33 +190,40 @@ main() {
|
||||
echo -e " ${INFO} Web Interface:\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
web_update=false
|
||||
echo -e " ${INFO} Web Interface:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
echo -e " ${INFO} Web Interface:\\t${COL_GREEN}up to date${COL_NC}"
|
||||
fi
|
||||
|
||||
local funcOutput
|
||||
funcOutput=$(get_binary_name) #Store output of get_binary_name here
|
||||
local binary
|
||||
binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL)
|
||||
# Allow the user to skip this check if they are using a self-compiled FTL binary from an unsupported architecture
|
||||
if [ "${skipFTL}" != true ]; then
|
||||
local funcOutput
|
||||
funcOutput=$(get_binary_name) #Store output of get_binary_name here
|
||||
local binary
|
||||
binary="pihole-FTL${funcOutput##*pihole-FTL}" #binary name will be the last line of the output of get_binary_name (it always begins with pihole-FTL)
|
||||
|
||||
if FTLcheckUpdate "${binary}" &>/dev/null; then
|
||||
FTL_update=true
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}"
|
||||
if FTLcheckUpdate "${binary}" &>/dev/null; then
|
||||
FTL_update=true
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}"
|
||||
else
|
||||
case $? in
|
||||
1)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_GREEN}up to date${COL_NC}"
|
||||
;;
|
||||
2)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_RED}Branch is not available.${COL_NC}\\n\\t\\t\\tUse ${COL_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch."
|
||||
exit 1
|
||||
;;
|
||||
3)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_RED}Something has gone wrong, cannot reach download server${COL_NC}"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_RED}Something has gone wrong, contact support${COL_NC}"
|
||||
exit 1
|
||||
esac
|
||||
FTL_update=false
|
||||
fi
|
||||
else
|
||||
case $? in
|
||||
1)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
|
||||
;;
|
||||
2)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Branch is not available.${COL_NC}\\n\\t\\t\\tUse ${COL_LIGHT_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch."
|
||||
;;
|
||||
3)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Something has gone wrong, cannot reach download server${COL_NC}"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Something has gone wrong, contact support${COL_NC}"
|
||||
exit 1
|
||||
esac
|
||||
echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}--skipFTL set - update check skipped${COL_NC}"
|
||||
FTL_update=false
|
||||
fi
|
||||
|
||||
@@ -228,7 +238,7 @@ main() {
|
||||
if [[ ! "${ftlBranch}" == "master" && ! "${ftlBranch}" == "development" ]]; then
|
||||
# Notify user that they are on a custom branch which might mean they they are lost
|
||||
# behind if a branch was merged to development and got abandoned
|
||||
printf " %b %bWarning:%b You are using FTL from a custom branch (%s) and might be missing future releases.\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}" "${ftlBranch}"
|
||||
printf " %b %bWarning:%b You are using FTL from a custom branch (%s) and might be missing future releases.\\n" "${INFO}" "${COL_RED}" "${COL_NC}" "${ftlBranch}"
|
||||
fi
|
||||
|
||||
if [[ "${core_update}" == false && "${web_update}" == false && "${FTL_update}" == false ]]; then
|
||||
@@ -258,7 +268,7 @@ main() {
|
||||
echo ""
|
||||
echo -e " ${INFO} Pi-hole Web Admin files out of date, updating local repo."
|
||||
getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}"
|
||||
echo -e " ${INFO} If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'"
|
||||
echo -e " ${INFO} If you had made any changes in '${ADMIN_INTERFACE_DIR}', they have been stashed using 'git stash'"
|
||||
fi
|
||||
|
||||
if [[ "${FTL_update}" == true ]]; then
|
||||
@@ -267,7 +277,14 @@ main() {
|
||||
fi
|
||||
|
||||
if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then
|
||||
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
|
||||
local addionalFlag
|
||||
|
||||
if [[ ${skipFTL} == true ]]; then
|
||||
addionalFlag="--skipFTL"
|
||||
else
|
||||
addionalFlag=""
|
||||
fi
|
||||
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --repair --unattended ${addionalFlag} || \
|
||||
echo -e "${basicError}" && exit 1
|
||||
fi
|
||||
|
||||
@@ -287,8 +304,15 @@ main() {
|
||||
exit 0
|
||||
}
|
||||
|
||||
if [[ "$1" == "--check-only" ]]; then
|
||||
CHECK_ONLY=true
|
||||
fi
|
||||
CHECK_ONLY=false
|
||||
skipFTL=false
|
||||
|
||||
# Check arguments
|
||||
for var in "$@"; do
|
||||
case "$var" in
|
||||
"--check-only") CHECK_ONLY=true ;;
|
||||
"--skipFTL") skipFTL=true ;;
|
||||
esac
|
||||
done
|
||||
|
||||
main
|
||||
|
||||
@@ -39,17 +39,21 @@ function get_remote_hash() {
|
||||
}
|
||||
|
||||
# Source the utils file for addOrEditKeyValPair()
|
||||
# shellcheck disable=SC1091
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
. /opt/pihole/utils.sh
|
||||
|
||||
ADMIN_INTERFACE_DIR=$(getFTLConfigValue "webserver.paths.webroot")$(getFTLConfigValue "webserver.paths.webhome")
|
||||
readonly ADMIN_INTERFACE_DIR
|
||||
|
||||
# Remove the below three legacy files if they exist
|
||||
rm -f "/etc/pihole/GitHubVersions"
|
||||
rm -f "/etc/pihole/localbranches"
|
||||
rm -f "/etc/pihole/localversions"
|
||||
|
||||
# Create new versions file if it does not exist
|
||||
VERSION_FILE="/etc/pihole/versions"
|
||||
touch "${VERSION_FILE}"
|
||||
|
||||
# Truncates the file to zero length if it exists to clear it up, otherwise creates an empty file.
|
||||
truncate -s 0 "${VERSION_FILE}"
|
||||
chmod 644 "${VERSION_FILE}"
|
||||
|
||||
# if /pihole.docker.tag file exists, we will use it's value later in this script
|
||||
@@ -85,13 +89,13 @@ addOrEditKeyValPair "${VERSION_FILE}" "GITHUB_CORE_HASH" "${GITHUB_CORE_HASH}"
|
||||
|
||||
# get Web versions
|
||||
|
||||
WEB_VERSION="$(get_local_version /var/www/html/admin)"
|
||||
WEB_VERSION="$(get_local_version "${ADMIN_INTERFACE_DIR}")"
|
||||
addOrEditKeyValPair "${VERSION_FILE}" "WEB_VERSION" "${WEB_VERSION}"
|
||||
|
||||
WEB_BRANCH="$(get_local_branch /var/www/html/admin)"
|
||||
WEB_BRANCH="$(get_local_branch "${ADMIN_INTERFACE_DIR}")"
|
||||
addOrEditKeyValPair "${VERSION_FILE}" "WEB_BRANCH" "${WEB_BRANCH}"
|
||||
|
||||
WEB_HASH="$(get_local_hash /var/www/html/admin)"
|
||||
WEB_HASH="$(get_local_hash "${ADMIN_INTERFACE_DIR}")"
|
||||
addOrEditKeyValPair "${VERSION_FILE}" "WEB_HASH" "${WEB_HASH}"
|
||||
|
||||
GITHUB_WEB_VERSION="$(get_remote_version web "${WEB_BRANCH}")"
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
# shellcheck disable=SC3043 #https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
|
||||
|
||||
# Pi-hole: A black hole for Internet advertisements
|
||||
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
|
||||
@@ -31,9 +30,6 @@ addOrEditKeyValPair() {
|
||||
local key="${2}"
|
||||
local value="${3}"
|
||||
|
||||
# touch file to prevent grep error if file does not exist yet
|
||||
touch "${file}"
|
||||
|
||||
if grep -q "^${key}=" "${file}"; then
|
||||
# Key already exists in file, modify the value
|
||||
sed -i "/^${key}=/c\\${key}=${value}" "${file}"
|
||||
@@ -74,7 +70,9 @@ getFTLPID() {
|
||||
# Example getFTLConfigValue dns.piholePTR
|
||||
#######################
|
||||
getFTLConfigValue(){
|
||||
pihole-FTL --config -q "${1}"
|
||||
# Pipe to cat to avoid pihole-FTL assuming this is an interactive command
|
||||
# returning colored output.
|
||||
pihole-FTL --config -q "${1}" | cat
|
||||
}
|
||||
|
||||
#######################
|
||||
@@ -87,9 +85,17 @@ getFTLConfigValue(){
|
||||
# setFTLConfigValue dns.upstreams '[ "8.8.8.8" , "8.8.4.4" ]'
|
||||
#######################
|
||||
setFTLConfigValue(){
|
||||
pihole-FTL --config "${1}" "${2}" >/dev/null
|
||||
if [[ $? -eq 5 ]]; then
|
||||
echo -e " ${CROSS} ${1} set by environment variable. Please unset it to use this function"
|
||||
exit 5
|
||||
fi
|
||||
local err
|
||||
{ pihole-FTL --config "${1}" "${2}" >/dev/null; err="$?"; } || true
|
||||
|
||||
case $err in
|
||||
0) ;;
|
||||
5)
|
||||
# FTL returns 5 if the value was set by an environment variable and is therefore read-only
|
||||
printf " %s %s set by environment variable. Please unset it to use this function\n" "${CROSS}" "${1}";
|
||||
exit 5;;
|
||||
*)
|
||||
printf " %s Failed to set %s. Try with sudo power\n" "${CROSS}" "${1}"
|
||||
exit 1
|
||||
esac
|
||||
}
|
||||
|
||||
@@ -8,20 +8,16 @@
|
||||
# This file is copyright under the latest version of the EUPL.
|
||||
# Please see LICENSE file for your rights under this license.
|
||||
|
||||
# Ignore warning about `local` being undefinded in POSIX
|
||||
# shellcheck disable=SC3043
|
||||
# https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
|
||||
|
||||
# Source the versions file poupulated by updatechecker.sh
|
||||
# Source the versions file populated by updatechecker.sh
|
||||
cachedVersions="/etc/pihole/versions"
|
||||
|
||||
if [ -f ${cachedVersions} ]; then
|
||||
# shellcheck disable=SC1090
|
||||
# shellcheck source=/dev/null
|
||||
. "$cachedVersions"
|
||||
else
|
||||
echo "Could not find /etc/pihole/versions. Running update now."
|
||||
pihole updatechecker
|
||||
# shellcheck disable=SC1090
|
||||
# shellcheck source=/dev/null
|
||||
. "$cachedVersions"
|
||||
fi
|
||||
|
||||
|
||||
@@ -43,8 +43,8 @@ CREATE TABLE adlist
|
||||
|
||||
CREATE TABLE adlist_by_group
|
||||
(
|
||||
adlist_id INTEGER NOT NULL REFERENCES adlist (id),
|
||||
group_id INTEGER NOT NULL REFERENCES "group" (id),
|
||||
adlist_id INTEGER NOT NULL REFERENCES adlist (id) ON DELETE CASCADE,
|
||||
group_id INTEGER NOT NULL REFERENCES "group" (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (adlist_id, group_id)
|
||||
);
|
||||
|
||||
@@ -66,7 +66,7 @@ CREATE TABLE info
|
||||
value TEXT NOT NULL
|
||||
);
|
||||
|
||||
INSERT INTO "info" VALUES('version','19');
|
||||
INSERT INTO "info" VALUES('version','20');
|
||||
/* This is a flag to indicate if gravity was restored from a backup
|
||||
false = not restored,
|
||||
failed = restoration failed due to no backup
|
||||
@@ -75,8 +75,8 @@ INSERT INTO "info" VALUES('gravity_restored','false');
|
||||
|
||||
CREATE TABLE domainlist_by_group
|
||||
(
|
||||
domainlist_id INTEGER NOT NULL REFERENCES domainlist (id),
|
||||
group_id INTEGER NOT NULL REFERENCES "group" (id),
|
||||
domainlist_id INTEGER NOT NULL REFERENCES domainlist (id) ON DELETE CASCADE,
|
||||
group_id INTEGER NOT NULL REFERENCES "group" (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (domainlist_id, group_id)
|
||||
);
|
||||
|
||||
@@ -91,8 +91,8 @@ CREATE TABLE client
|
||||
|
||||
CREATE TABLE client_by_group
|
||||
(
|
||||
client_id INTEGER NOT NULL REFERENCES client (id),
|
||||
group_id INTEGER NOT NULL REFERENCES "group" (id),
|
||||
client_id INTEGER NOT NULL REFERENCES client (id) ON DELETE CASCADE,
|
||||
group_id INTEGER NOT NULL REFERENCES "group" (id) ON DELETE CASCADE,
|
||||
PRIMARY KEY (client_id, group_id)
|
||||
);
|
||||
|
||||
@@ -111,7 +111,7 @@ CREATE TRIGGER tr_domainlist_update AFTER UPDATE ON domainlist
|
||||
UPDATE domainlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
|
||||
END;
|
||||
|
||||
CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
CREATE VIEW vw_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
@@ -119,7 +119,7 @@ CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_gr
|
||||
AND domainlist.type = 0
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
CREATE VIEW vw_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
@@ -127,7 +127,7 @@ CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_gr
|
||||
AND domainlist.type = 1
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
CREATE VIEW vw_regex_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
@@ -135,7 +135,7 @@ CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist
|
||||
AND domainlist.type = 2
|
||||
ORDER BY domainlist.id;
|
||||
|
||||
CREATE VIEW vw_regex_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
CREATE VIEW vw_regex_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
|
||||
FROM domainlist
|
||||
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
|
||||
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
# Source utils.sh for getFTLConfigValue()
|
||||
PI_HOLE_SCRIPT_DIR='/opt/pihole'
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck disable=SC1090
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
. "${utilsfile}"
|
||||
|
||||
# Get file paths
|
||||
|
||||
@@ -3,32 +3,40 @@
|
||||
# Source utils.sh for getFTLConfigValue()
|
||||
PI_HOLE_SCRIPT_DIR='/opt/pihole'
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck disable=SC1090
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
. "${utilsfile}"
|
||||
|
||||
# Get file paths
|
||||
FTL_PID_FILE="$(getFTLConfigValue files.pid)"
|
||||
FTL_LOG_FILE="$(getFTLConfigValue files.log.ftl)"
|
||||
PIHOLE_LOG_FILE="$(getFTLConfigValue files.log.dnsmasq)"
|
||||
WEBSERVER_LOG_FILE="$(getFTLConfigValue files.log.webserver)"
|
||||
FTL_PID_FILE="${FTL_PID_FILE:-/run/pihole-FTL.pid}"
|
||||
FTL_LOG_FILE="${FTL_LOG_FILE:-/var/log/pihole/FTL.log}"
|
||||
PIHOLE_LOG_FILE="${PIHOLE_LOG_FILE:-/var/log/pihole/pihole.log}"
|
||||
WEBSERVER_LOG_FILE="${WEBSERVER_LOG_FILE:-/var/log/pihole/webserver.log}"
|
||||
|
||||
# Ensure that permissions are set so that pihole-FTL can edit all necessary files
|
||||
# shellcheck disable=SC2174
|
||||
mkdir -pm 0640 /var/log/pihole
|
||||
chown -R pihole:pihole /etc/pihole /var/log/pihole
|
||||
chmod -R 0640 /var/log/pihole
|
||||
chmod -R 0660 /etc/pihole
|
||||
mkdir -p /var/log/pihole
|
||||
chown -R pihole:pihole /etc/pihole/ /var/log/pihole/
|
||||
|
||||
# Logrotate config file need to be owned by root and must not be writable by group and others
|
||||
chown root:root /etc/pihole/logrotate
|
||||
chmod 0644 /etc/pihole/logrotate
|
||||
|
||||
# allow all users to enter the directories
|
||||
chmod 0755 /etc/pihole /var/log/pihole
|
||||
# allow all users read version file (and use pihole -v)
|
||||
touch /etc/pihole/versions
|
||||
chmod 0644 /etc/pihole/versions
|
||||
|
||||
# allow pihole to access subdirs in /etc/pihole (sets execution bit on dirs)
|
||||
# credits https://stackoverflow.com/a/11512211
|
||||
find /etc/pihole/ -type d -exec chmod 0755 {} \;
|
||||
find /etc/pihole/ /var/log/pihole/ -type d -exec chmod 0755 {} +
|
||||
# Set all files (except TLS-related ones) to u+rw g+r
|
||||
find /etc/pihole/ /var/log/pihole/ -type f ! \( -name '*.pem' -o -name '*.crt' \) -exec chmod 0640 {} +
|
||||
# Set TLS-related files to a more restrictive u+rw *only* (they may contain private keys)
|
||||
find /etc/pihole/ -type f \( -name '*.pem' -o -name '*.crt' \) -exec chmod 0600 {} +
|
||||
|
||||
# Logrotate config file need to be owned by root
|
||||
chown root:root /etc/pihole/logrotate
|
||||
|
||||
# Touch files to ensure they exist (create if non-existing, preserve if existing)
|
||||
[ -f "${FTL_PID_FILE}" ] || install -D -m 644 -o pihole -g pihole /dev/null "${FTL_PID_FILE}"
|
||||
[ -f /var/log/pihole/FTL.log ] || install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/FTL.log
|
||||
[ -f /var/log/pihole/pihole.log ] || install -m 640 -o pihole -g pihole /dev/null /var/log/pihole/pihole.log
|
||||
[ -f "${FTL_LOG_FILE}" ] || install -m 640 -o pihole -g pihole /dev/null "${FTL_LOG_FILE}"
|
||||
[ -f "${PIHOLE_LOG_FILE}" ] || install -m 640 -o pihole -g pihole /dev/null "${PIHOLE_LOG_FILE}"
|
||||
[ -f "${WEBSERVER_LOG_FILE}" ] || install -m 640 -o pihole -g pihole /dev/null "${WEBSERVER_LOG_FILE}"
|
||||
[ -f /etc/pihole/dhcp.leases ] || install -m 644 -o pihole -g pihole /dev/null /etc/pihole/dhcp.leases
|
||||
|
||||
40
advanced/Templates/pihole-FTL.openrc
Normal file
40
advanced/Templates/pihole-FTL.openrc
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/sbin/openrc-run
|
||||
# shellcheck shell=sh disable=SC2034
|
||||
|
||||
: "${PI_HOLE_SCRIPT_DIR:=/opt/pihole}"
|
||||
|
||||
command="/usr/bin/pihole-FTL"
|
||||
command_user="pihole:pihole"
|
||||
supervisor=supervise-daemon
|
||||
command_args_foreground="-f"
|
||||
command_background=true
|
||||
pidfile="/run/${RC_SVCNAME}_openrc.pid"
|
||||
extra_started_commands="reload"
|
||||
|
||||
respawn_max=5
|
||||
respawn_period=60
|
||||
capabilities="^CAP_NET_BIND_SERVICE,^CAP_NET_RAW,^CAP_NET_ADMIN,^CAP_SYS_NICE,^CAP_IPC_LOCK,^CAP_CHOWN,^CAP_SYS_TIME"
|
||||
|
||||
depend() {
|
||||
want net
|
||||
provide dns
|
||||
}
|
||||
|
||||
checkconfig() {
|
||||
$command -f test
|
||||
}
|
||||
|
||||
start_pre() {
|
||||
sh "${PI_HOLE_SCRIPT_DIR}/pihole-FTL-prestart.sh"
|
||||
}
|
||||
|
||||
stop_post() {
|
||||
sh "${PI_HOLE_SCRIPT_DIR}/pihole-FTL-poststop.sh"
|
||||
}
|
||||
|
||||
reload() {
|
||||
checkconfig || return $?
|
||||
ebegin "Reloading ${RC_SVCNAME}"
|
||||
start-stop-daemon --signal HUP --pidfile "${pidfile}"
|
||||
eend $?
|
||||
}
|
||||
@@ -12,7 +12,7 @@
|
||||
# Source utils.sh for getFTLConfigValue(), getFTLPID()
|
||||
PI_HOLE_SCRIPT_DIR="/opt/pihole"
|
||||
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
|
||||
# shellcheck disable=SC1090
|
||||
# shellcheck source="./advanced/Scripts/utils.sh"
|
||||
. "${utilsfile}"
|
||||
|
||||
|
||||
@@ -57,13 +57,16 @@ start() {
|
||||
stop() {
|
||||
if is_running; then
|
||||
kill "${FTL_PID}"
|
||||
for i in 1 2 3 4 5; do
|
||||
# Give FTL 120 seconds to gracefully stop
|
||||
i=1
|
||||
while [ "${i}" -le 120 ]; do
|
||||
if ! is_running; then
|
||||
break
|
||||
fi
|
||||
|
||||
printf "."
|
||||
sleep 1
|
||||
i=$((i + 1))
|
||||
done
|
||||
echo
|
||||
|
||||
|
||||
@@ -17,18 +17,18 @@ StartLimitIntervalSec=60s
|
||||
|
||||
[Service]
|
||||
User=pihole
|
||||
PermissionsStartOnly=true
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_NET_ADMIN CAP_SYS_NICE CAP_IPC_LOCK CAP_CHOWN CAP_SYS_TIME
|
||||
|
||||
ExecStartPre=/opt/pihole/pihole-FTL-prestart.sh
|
||||
# Run prestart with elevated permissions
|
||||
ExecStartPre=+/opt/pihole/pihole-FTL-prestart.sh
|
||||
ExecStart=/usr/bin/pihole-FTL -f
|
||||
Restart=on-failure
|
||||
RestartSec=5s
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
ExecStopPost=/opt/pihole/pihole-FTL-poststop.sh
|
||||
ExecStopPost=+/opt/pihole/pihole-FTL-poststop.sh
|
||||
|
||||
# Use graceful shutdown with a reasonable timeout
|
||||
TimeoutStopSec=10s
|
||||
TimeoutStopSec=120s
|
||||
|
||||
# Make /usr, /boot, /etc and possibly some more folders read-only...
|
||||
ProtectSystem=full
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
# The flush script will use logrotate if available
|
||||
# parameter "once": logrotate only once (default is twice)
|
||||
# parameter "quiet": don't print messages
|
||||
00 00 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole flush once quiet
|
||||
00 00 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole logrotate quiet
|
||||
|
||||
@reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate
|
||||
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
_pihole() {
|
||||
local cur prev opts opts_checkout opts_debug opts_logging opts_query opts_update opts_version
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
prev2="${COMP_WORDS[COMP_CWORD-2]}"
|
||||
|
||||
case "${prev}" in
|
||||
"pihole")
|
||||
opts="allow allow-regex allow-wild deny checkout debug disable enable flush help logging query reconfigure regex reloaddns reloadlists status tail uninstall updateGravity updatePihole version wildcard arpflush api"
|
||||
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
|
||||
;;
|
||||
"allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild")
|
||||
opts_lists="\not \--delmode \--quiet \--list \--help"
|
||||
COMPREPLY=( $(compgen -W "${opts_lists}" -- ${cur}) )
|
||||
;;
|
||||
"checkout")
|
||||
opts_checkout="core ftl web master dev"
|
||||
COMPREPLY=( $(compgen -W "${opts_checkout}" -- ${cur}) )
|
||||
;;
|
||||
"debug")
|
||||
opts_debug="-a"
|
||||
COMPREPLY=( $(compgen -W "${opts_debug}" -- ${cur}) )
|
||||
;;
|
||||
"logging")
|
||||
opts_logging="on off 'off noflush'"
|
||||
COMPREPLY=( $(compgen -W "${opts_logging}" -- ${cur}) )
|
||||
;;
|
||||
"query")
|
||||
opts_query="--partial --all"
|
||||
COMPREPLY=( $(compgen -W "${opts_query}" -- ${cur}) )
|
||||
;;
|
||||
"updatePihole"|"-up")
|
||||
opts_update="--check-only"
|
||||
COMPREPLY=( $(compgen -W "${opts_update}" -- ${cur}) )
|
||||
;;
|
||||
"core"|"admin"|"ftl")
|
||||
if [[ "$prev2" == "checkout" ]]; then
|
||||
opts_checkout="master dev"
|
||||
COMPREPLY=( $(compgen -W "${opts_checkout}" -- ${cur}) )
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _pihole pihole
|
||||
9
advanced/bash-completion/pihole-ftl.bash
Normal file
9
advanced/bash-completion/pihole-ftl.bash
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Bash completion script for pihole-FTL
|
||||
#
|
||||
# This completion script provides tab completion for pihole-FTL CLI flags and commands.
|
||||
# It uses the `pihole-FTL --complete` command to generate the completion options.
|
||||
_complete_FTL() { mapfile -t COMPREPLY < <(pihole-FTL --complete "${COMP_WORDS[@]}"); }
|
||||
|
||||
complete -F _complete_FTL pihole-FTL
|
||||
59
advanced/bash-completion/pihole.bash
Normal file
59
advanced/bash-completion/pihole.bash
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# Bash completion script for pihole
|
||||
#
|
||||
_pihole() {
|
||||
local cur prev prev2 opts opts_lists opts_checkout opts_debug opts_logging opts_query opts_update opts_networkflush
|
||||
COMPREPLY=()
|
||||
cur="${COMP_WORDS[COMP_CWORD]}"
|
||||
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
||||
prev2="${COMP_WORDS[COMP_CWORD-2]}"
|
||||
|
||||
case "${prev}" in
|
||||
"pihole")
|
||||
opts="allow allow-regex allow-wild deny checkout debug disable enable flush help logging query repair regex reloaddns reloadlists setpassword status tail uninstall updateGravity updatePihole version wildcard networkflush api"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts}" -- "${cur}")
|
||||
;;
|
||||
"allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild")
|
||||
opts_lists="\not \--delmode \--quiet \--list \--help"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_lists}" -- "${cur}")
|
||||
;;
|
||||
"checkout")
|
||||
opts_checkout="core ftl web master dev"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_checkout}" -- "${cur}")
|
||||
;;
|
||||
"debug")
|
||||
opts_debug="-a"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_debug}" -- "${cur}")
|
||||
;;
|
||||
"logging")
|
||||
opts_logging="on off 'off noflush'"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_logging}" -- "${cur}")
|
||||
;;
|
||||
"query")
|
||||
opts_query="--partial --all"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_query}" -- "${cur}")
|
||||
;;
|
||||
"updatePihole"|"-up")
|
||||
opts_update="--check-only"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_update}" -- "${cur}")
|
||||
;;
|
||||
"networkflush")
|
||||
opts_networkflush="--arp"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_networkflush}" -- "${cur}")
|
||||
;;
|
||||
"core"|"web"|"ftl")
|
||||
if [[ "$prev2" == "checkout" ]]; then
|
||||
opts_checkout="master development"
|
||||
mapfile -t COMPREPLY < <(compgen -W "${opts_checkout}" -- "${cur}")
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
return 0
|
||||
}
|
||||
complete -F _pihole pihole
|
||||
Reference in New Issue
Block a user