1
0
mirror of https://github.com/pi-hole/pi-hole synced 2025-01-10 16:10:56 +00:00

first functions with pretty colors. check OS, setupVars, and contents of .d dirs

This commit is contained in:
Jacob Salmela 2017-05-20 01:34:13 -05:00
parent 2f2825f15e
commit 6fa00e7cc8
No known key found for this signature in database
GPG Key ID: 1962FF1A5046135E

View File

@ -9,7 +9,9 @@
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
# causes a pipeline to produce a failure return code if any command errors.
# Normally, pipelines only return a failure if the last command errors.
# In combination with set -e, this will make your script exit if any command in a pipeline errors.
set -o pipefail set -o pipefail
######## GLOBAL VARS ######## ######## GLOBAL VARS ########
@ -28,515 +30,147 @@ PIHOLEGITDIR="/etc/.pihole/"
ADMINGITDIR="/var/www/html/admin/" ADMINGITDIR="/var/www/html/admin/"
WHITELISTMATCHES="/tmp/whitelistmatches.list" WHITELISTMATCHES="/tmp/whitelistmatches.list"
readonly FTLLOG="/var/log/pihole-FTL.log" readonly FTLLOG="/var/log/pihole-FTL.log"
coltable=/opt/pihole/COL_TABLE
TIMEOUT=60 if [[ -f ${coltable} ]]; then
# Header info and introduction source ${coltable}
cat << EOM else
::: Beginning Pi-hole debug at $(date)! COL_NC='\e[0m' # No Color
::: COL_YELLOW='\e[1;33m'
::: This process collects information from your Pi-hole, and optionally uploads COL_LIGHT_PURPLE='\e[1;35m'
::: it to a unique and random directory on tricorder.pi-hole.net. COL_CYAN='\e[0;36m'
::: TICK="[${COL_LIGHT_GREEN}${COL_NC}]"
::: NOTE: All log files auto-delete after 48 hours and ONLY the Pi-hole developers CROSS="[${COL_LIGHT_RED}${COL_NC}]"
::: can access your data via the given token. We have taken these extra steps to INFO="[i]"
::: secure your data and will work to further reduce any personal information gathered. DONE="${COL_LIGHT_GREEN} done!${COL_NC}"
::: OVER="\r\033[K"
::: Please read and note any issues, and follow any directions advised during this process. fi
EOM
source ${VARSFILE} echo_succes_or_fail() {
local message="${1}"
### Private functions exist here ### if [ $? -eq 0 ]; then
log_write() { echo -e " ${TICK} ${message}"
echo "${@}" >&3
}
log_echo() {
case ${1} in
-n)
echo -n "::: ${2}"
log_write "${2}"
;;
-r)
echo "::: ${2}"
log_write "${2}"
;;
-l)
echo "${2}"
log_write "${2}"
;;
*)
echo "::: ${1}"
log_write "${1}"
esac
}
header_write() {
log_echo ""
log_echo "---= ${1}"
log_write ""
}
file_parse() {
while read -r line; do
if [ ! -z "${line}" ]; then
[[ "${line}" =~ ^#.*$ || ! "${line}" || "${line}" == "WEBPASSWORD="* ]] && continue
log_write "${line}"
fi
done < "${1}"
log_write ""
}
block_parse() {
log_write "${1}"
}
lsof_parse() {
local user
local process
user=$(echo ${1} | cut -f 3 -d ' ' | cut -c 2-)
process=$(echo ${1} | cut -f 2 -d ' ' | cut -c 2-)
[[ ${2} -eq ${process} ]] \
&& echo "::: Correctly configured." \
|| log_echo "::: Failure: Incorrectly configured daemon."
log_write "Found user ${user} with process ${process}"
}
version_check() {
header_write "Detecting Installed Package Versions:"
local error_found
local pi_hole_ver
local pi_hole_branch
local pi_hole_commit
local admin_ver
local admin_branch
local admin_commit
local light_ver
local php_ver
local status
error_found=0
cd "${PIHOLEGITDIR}" &> /dev/null || \
{ status="Pi-hole git directory not found."; error_found=1; }
if git status &> /dev/null; then
pi_hole_ver=$(git describe --tags --abbrev=0)
pi_hole_branch=$(git rev-parse --abbrev-ref HEAD)
pi_hole_commit=$(git describe --long --dirty --tags --always)
log_echo -r "Pi-hole: ${pi_hole_ver:-Untagged} (${pi_hole_branch:-Detached}:${pi_hole_commit})"
else else
status=${status:-"Pi-hole repository damaged."} echo -e " ${CROSS} ${message}"
error_found=1
fi
if [[ "${status}" ]]; then
log_echo "${status}"
unset status
fi fi
}
cd "${ADMINGITDIR}" || \ initiate_debug() {
{ status="Pi-hole Dashboard git directory not found."; error_found=1; } # Clear the screen so the debug log is readable
if git status &> /dev/null; then clear
admin_ver=$(git describe --tags --abbrev=0) echo -e "${COL_LIGHT_PURPLE}*** [ INITIALIZING ]${COL_NC}"
admin_branch=$(git rev-parse --abbrev-ref HEAD) echo -e " ${INFO} $(date "+%Y-%m-%d:%H:%M:%S") debug log has been initiated."
admin_commit=$(git describe --long --dirty --tags --always) }
log_echo -r "Pi-hole Dashboard: ${admin_ver:-Untagged} (${admin_branch:-Detached}:${admin_commit})"
# This is a function for visually displaying the curent test that is being run.
# Accepts one variable: the name of what is being diagnosed
echo_current_diagnostic() {
# Colors are used for visually distinguishing each test in the output
echo -e "\n${COL_LIGHT_PURPLE}*** [ DIAGNOSING ]:${COL_NC} ${1}"
}
if_file_exists() {
local file_to_test="${1}"
# If the file is readable
if [[ -r "${file_to_test}" ]]; then
# Return success
return 0
else else
status=${status:-"Pi-hole Dashboard repository damaged."} # Otherwise, return a failure
error_found=1 return 1
fi
if [[ "${status}" ]]; then
log_echo "${status}"
unset status
fi fi
}
if light_ver=$(lighttpd -v |& head -n1 | cut -d " " -f1); then get_distro_attributes() {
log_echo -r "${light_ver}" # Put the current Internal Field Separator into another variable so it can be restored later
else OLD_IFS="$IFS"
log_echo "lighttpd not installed." # Store the distro info in an array and make it global since the OS won't change,
error_found=1 # but we'll keep it within the function for better unit testing
fi IFS=$'\r\n' command eval 'distro_info=( $(cat /etc/*release) )'
if php_ver=$(php -v |& head -n1); then
log_echo -r "${php_ver}"
else
log_echo "PHP not installed."
error_found=1
fi
return "${error_found}" local distro_attribute
for distro_attribute in "${distro_info[@]}"; do
# Display the information with the ${INFO} icon
# No need to show the support URLs so they are grepped out
echo " ${INFO} ${distro_attribute}" | grep -v "_URL" | tr -d '"'
done
# Set the IFS back to what it was
IFS="$OLD_IFS"
}
diagnose_operating_system() {
# Display the current test that is running
echo_current_diagnostic "Operating system"
# If there is a /etc/*release file, it's probably a supported operating system, so we can
if_file_exists /etc/*release && \
# display the attributes to the user
get_distro_attributes || \
# If it doesn't exist, it's not a system we currently support
echo -e " ${CROSS} ${COL_LIGHT_RED}Distribution unknown -- most likely you are on an unsupported platform and may run into issues.${COL_NC}
${INFO} ${COL_LIGHT_RED}Please see${COL_NC}: ${COL_CYAN}https://discourse.pi-hole.net/t/hardware-software-requirements/273${COL_NC}"
}
parse_file() {
local filename="${1}"
OLD_IFS="$IFS"
IFS=$'\r\n' command eval 'file_info=( $(cat "${filename}") )'
local file_lines
for file_lines in "${file_info[@]}"; do
# Display the information with the ${INFO} icon
# No need to show the support URLs so they are grepped out
echo " ${INFO} ${file_lines}"
done
# Set the IFS back to what it was
IFS="$OLD_IFS"
}
diagnose_setup_variables() {
# Display the current test that is running
echo_current_diagnostic "Setup variables"
# If the variable file exists,
if_file_exists "${VARSFILE}" && \
# source it
echo -e " ${INFO} Sourcing ${VARSFILE}...";
source ${VARSFILE};
# and display a green check mark with ${DONE}
echo_succes_or_fail "${VARSFILE} is readable and has been sourced." || \
# Othwerwise, error out
echo_succes_or_fail "${VARSFILE} is not readable.
${INFO} $(ls -l ${VARSFILE} 2>/dev/null)";
parse_file "${VARSFILE}"
} }
dir_check() { dir_check() {
header_write "Detecting contents of ${1}:" local directory="${1}"
for file in $1*; do echo_current_diagnostic "contents of ${directory}"
header_write "File ${file} found" for filename in "${directory}"*; do
echo -n "::: Parsing..." if_file_exists "${filename}" && \
file_parse "${file}" echo_succes_or_fail "Files detected" || \
echo "done" echo_succes_or_fail "directory does not exist"
done
echo ":::"
}
files_check() {
#Check non-zero length existence of ${1}
header_write "Detecting existence of ${1}:"
local search_file="${1}"
if [[ -s ${search_file} ]]; then
echo -n "::: File exists, parsing..."
file_parse "${search_file}"
echo "done"
return 0
else
log_echo "${1} not found!"
return 1
fi
echo ":::"
}
source_file() {
local file_found=$(files_check "${1}") \
&& (source "${1}" &> /dev/null && echo "${file_found} and was successfully sourced") \
|| log_echo -l "${file_found} and could not be sourced"
}
distro_check() {
local soft_fail
header_write "Detecting installed OS Distribution"
soft_fail=0
local distro="$(cat /etc/*release)" && block_parse "${distro}" || (log_echo "Distribution details not found." && soft_fail=1)
return "${soft_fail}"
}
processor_check() {
header_write "Checking processor variety"
log_write $(uname -m) && return 0 || return 1
}
ipv6_check() {
# Check if system is IPv6 enabled, for use in other functions
if [[ $IPV6_ADDRESS ]]; then
ls /proc/net/if_inet6 &>/dev/null
return 0
else
return 1
fi
}
ip_check() {
local protocol=${1}
local gravity=${2}
header_write "Checking IPv${protocol} Stack"
local ip_addr_list="$(ip -${protocol} addr show dev ${PIHOLE_INTERFACE} | awk -F ' ' '{ for(i=1;i<=NF;i++) if ($i ~ '/^inet/') print $(i+1) }')"
if [[ -n ${ip_addr_list} ]]; then
log_write "IPv${protocol} on ${PIHOLE_INTERFACE}"
log_write "Gravity configured for: ${2:-NOT CONFIGURED}"
log_write "----"
log_write "${ip_addr_list}"
echo "::: IPv${protocol} addresses located on ${PIHOLE_INTERFACE}"
ip_ping_check ${protocol}
return $(( 0 + $? ))
else
log_echo "No IPv${protocol} found on ${PIHOLE_INTERFACE}"
return 1
fi
}
ip_ping_check() {
local protocol=${1}
local cmd
if [[ ${protocol} == "6" ]]; then
cmd="ping6"
g_addr="2001:4860:4860::8888"
else
cmd="ping"
g_addr="8.8.8.8"
fi
local ip_def_gateway=$(ip -${protocol} route | grep default | cut -d ' ' -f 3)
if [[ -n ${ip_def_gateway} ]]; then
echo -n "::: Pinging default IPv${protocol} gateway: "
if ! ping_gateway="$(${cmd} -q -W 3 -c 3 -n ${ip_def_gateway} -I ${PIHOLE_INTERFACE} | tail -n 3)"; then
log_echo "Gateway did not respond."
return 1
else
log_echo "Gateway responded."
log_write "${ping_gateway}"
fi
echo -n "::: Pinging Internet via IPv${protocol}: "
if ! ping_inet="$(${cmd} -q -W 3 -c 3 -n ${g_addr} -I ${PIHOLE_INTERFACE} | tail -n 3)"; then
log_echo "Query did not respond."
return 1
else
log_echo "Query responded."
log_write "${ping_inet}"
fi
else
log_echo " No gateway detected."
fi
return 0
}
port_check() {
local lsof_value
lsof_value=$(lsof -i ${1}:${2} -FcL | tr '\n' ' ') \
&& lsof_parse "${lsof_value}" "${3}" \
|| log_echo "Failure: IPv${1} Port not in use"
}
daemon_check() {
# Check for daemon ${1} on port ${2}
header_write "Daemon Process Information"
echo "::: Checking ${2} port for ${1} listener."
if [[ ${IPV6_READY} ]]; then
port_check 6 "${2}" "${1}"
fi
lsof_value=$(lsof -i 4:${2} -FcL | tr '\n' ' ') \
port_check 4 "${2}" "${1}"
}
testResolver() {
local protocol="${1}"
header_write "Resolver Functions Check (IPv${protocol})"
local IP="${2}"
local g_addr
local l_addr
local url
local testurl
local localdig
local piholedig
local remotedig
if [[ ${protocol} == "6" ]]; then
g_addr="2001:4860:4860::8888"
l_addr="::1"
r_type="AAAA"
else
g_addr="8.8.8.8"
l_addr="127.0.0.1"
r_type="A"
fi
# Find a blocked url that has not been whitelisted.
url=$(shuf -n 1 "${GRAVITYFILE}" | awk -F ' ' '{ print $2 }')
testurl="${url:-doubleclick.com}"
log_write "Resolution of ${testurl} from Pi-hole (${l_addr}):"
if localdig=$(dig -"${protocol}" "${testurl}" @${l_addr} +short "${r_type}"); then
log_write "${localdig}"
else
log_write "Failed to resolve ${testurl} on Pi-hole (${l_addr})"
fi
log_write ""
log_write "Resolution of ${testurl} from Pi-hole (${IP}):"
if piholedig=$(dig -"${protocol}" "${testurl}" @"${IP}" +short "${r_type}"); then
log_write "${piholedig}"
else
log_write "Failed to resolve ${testurl} on Pi-hole (${IP})"
fi
log_write ""
log_write "Resolution of ${testurl} from ${g_addr}:"
if remotedig=$(dig -"${protocol}" "${testurl}" @${g_addr} +short "${r_type}"); then
log_write "${remotedig:-NXDOMAIN}"
else
log_write "Failed to resolve ${testurl} on upstream server ${g_addr}"
fi
log_write ""
}
testChaos(){
# Check Pi-hole specific records
log_write "Pi-hole dnsmasq specific records lookups"
log_write "Cache Size:"
log_write $(dig +short chaos txt cachesize.bind)
log_write "Upstream Servers:"
log_write $(dig +short chaos txt servers.bind)
log_write ""
}
checkProcesses() {
header_write "Processes Check"
echo "::: Logging status of lighttpd, dnsmasq and pihole-FTL..."
PROCESSES=( lighttpd dnsmasq pihole-FTL )
for i in "${PROCESSES[@]}"; do
log_write "Status for ${i} daemon:"
log_write $(systemctl is-active "${i}")
done
log_write ""
}
debugLighttpd() {
echo "::: Checking for necessary lighttpd files."
files_check "${LIGHTTPDFILE}"
files_check "${LIGHTTPDERRFILE}"
echo ":::"
}
countdown() {
local tuvix
tuvix=${TIMEOUT}
printf "::: Logging will automatically teminate in %s seconds\n" "${TIMEOUT}"
while [ $tuvix -ge 1 ]
do
printf ":::\t%s seconds left. " "${tuvix}"
if [[ -z "${WEBCALL}" ]]; then
printf "\r"
else
printf "\n"
fi
sleep 5
tuvix=$(( tuvix - 5 ))
done done
} }
# Continuously append the pihole.log file to the pihole_debug.log file list_files_in_dir() {
dumpPiHoleLog() { local dir_to_parse="${1}"
trap '{ echo -e "\n::: Finishing debug write from interrupt... Quitting!" ; exit 1; }' INT local filename
echo "::: " files_found=( $(ls "${dir_to_parse}") )
echo "::: --= User Action Required =--" for each_file in "${files_found[@]}"; do
echo -e "::: Try loading a site that you are having trouble with now from a client web browser.. \n:::\t(Press CTRL+C to finish logging.)" # Display the information with the ${INFO} icon
header_write "pihole.log" echo " ${INFO} ${each_file}"
if [ -e "${PIHOLELOG}" ]; then done
# Dummy process to use for flagging down tail to terminate
countdown &
tail -n0 -f --pid=$! "${PIHOLELOG}" >&4
else
log_write "No pihole.log file found!"
printf ":::\tNo pihole.log file found!\n"
fi
} }
# Anything to be done after capturing of pihole.log terminates check_dnsmasq_d() {
finalWork() { local directory=/etc/dnsmasq.d
local tricorder dir_check "${directory}"
echo "::: Finshed debugging!" list_files_in_dir "${directory}"
# Ensure the file exists, create if not, clear if exists.
truncate --size=0 "${DEBUG_LOG}"
chmod 644 ${DEBUG_LOG}
chown "$USER":pihole ${DEBUG_LOG}
# copy working temp file to final log location
cat /proc/$$/fd/3 >> "${DEBUG_LOG}"
# Straight dump of tailing the logs, can sanitize later if needed.
cat /proc/$$/fd/4 >> "${DEBUG_LOG}"
echo "::: The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only."
if [[ "${AUTOMATED}" ]]; then
echo "::: Debug script running in automated mode, uploading log to tricorder..."
tricorder=$(cat /var/log/pihole_debug.log | nc tricorder.pi-hole.net 9999)
else
read -r -p "::: Would you like to upload the log? [y/N] " response
case ${response} in
[yY][eE][sS]|[yY])
tricorder=$(cat /var/log/pihole_debug.log | nc tricorder.pi-hole.net 9999)
;;
*)
echo "::: Log will NOT be uploaded to tricorder."
;;
esac
fi
# Check if tricorder.pi-hole.net is reachable and provide token.
if [ -n "${tricorder}" ]; then
echo "::: ---=== Your debug token is : ${tricorder} Please make a note of it. ===---"
echo "::: Contact the Pi-hole team with your token for assistance."
echo "::: Thank you."
else
echo "::: There was an error uploading your debug log."
echo "::: Please try again or contact the Pi-hole team for assistance."
fi
echo "::: A local copy of the Debug log can be found at : /var/log/pihole_debug.log"
} }
### END FUNCTIONS ### initiate_debug
# Create temporary file for log diagnose_operating_system
TEMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX) diagnose_setup_variables
# Open handle 3 for templog check_dnsmasq_d
exec 3>"$TEMPLOG"
# Delete templog, but allow for addressing via file handle.
rm "$TEMPLOG"
# Create temporary file for logdump using file handle 4
DUMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX)
exec 4>"$DUMPLOG"
rm "$DUMPLOG"
# Gather version of required packages / repositories
version_check || echo "REQUIRED FILES MISSING"
# Check for newer setupVars storage file
source_file "/etc/pihole/setupVars.conf"
# Gather information about the running distribution
distro_check || echo "Distro Check soft fail"
# Gather processor type
processor_check || echo "Processor Check soft fail"
ip_check 6 ${IPV6_ADDRESS}
ip_check 4 ${IPV4_ADDRESS}
daemon_check lighttpd http
daemon_check dnsmasq domain
daemon_check pihole-FTL 4711
checkProcesses
# Check local/IP/Google for IPv4 Resolution
testResolver 4 "${IPV4_ADDRESS%/*}"
# If IPv6 enabled, check resolution
if [[ "${IPV6_ADDRESS}" ]]; then
testResolver 6 "${IPV6_ADDRESS%/*}"
fi
# Poll dnsmasq Pi-hole specific queries
testChaos
debugLighttpd
files_check "${DNSMASQFILE}"
dir_check "${DNSMASQCONFDIR}"
files_check "${WHITELISTFILE}"
files_check "${BLACKLISTFILE}"
files_check "${ADLISTFILE}"
header_write "Analyzing gravity.list"
gravity_length=$(grep -c ^ "${GRAVITYFILE}") \
&& log_write "${GRAVITYFILE} is ${gravity_length} lines long." \
|| log_echo "Warning: No gravity.list file found!"
header_write "Analyzing pihole.log"
pihole_length=$(grep -c ^ "${PIHOLELOG}") \
&& log_write "${PIHOLELOG} is ${pihole_length} lines long." \
|| log_echo "Warning: No pihole.log file found!"
pihole_size=$(du -h "${PIHOLELOG}" | awk '{ print $1 }') \
&& log_write "${PIHOLELOG} is ${pihole_size}." \
|| log_echo "Warning: No pihole.log file found!"
header_write "Analyzing pihole-FTL.log"
FTL_length=$(grep -c ^ "${FTLLOG}") \
&& log_write "${FTLLOG} is ${FTL_length} lines long." \
|| log_echo "Warning: No pihole-FTL.log file found!"
FTL_size=$(du -h "${FTLLOG}" | awk '{ print $1 }') \
&& log_write "${FTLLOG} is ${FTL_size}." \
|| log_echo "Warning: No pihole-FTL.log file found!"
tail -n50 "${FTLLOG}" >&3
trap finalWork EXIT
### Method calls for additional logging ###
dumpPiHoleLog