Merge branch 'development' into release/v4.3

pull/2750/head
Dan Schaper 5 years ago committed by GitHub
commit 7de6bbca61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -18,8 +18,6 @@
# WITHIN /etc/dnsmasq.d/yourname.conf #
###############################################################################
addn-hosts=/etc/pihole/gravity.list
addn-hosts=/etc/pihole/black.list
addn-hosts=/etc/pihole/local.list
domain-needed

@ -11,46 +11,45 @@
# Globals
basename=pihole
piholeDir=/etc/"${basename}"
whitelist="${piholeDir}"/whitelist.txt
blacklist="${piholeDir}"/blacklist.txt
gravityDBfile="${piholeDir}/gravity.db"
readonly regexlist="/etc/pihole/regex.list"
reload=false
addmode=true
verbose=true
wildcard=false
web=false
domList=()
listMain=""
listAlt=""
listType=""
listname=""
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
helpFunc() {
if [[ "${listMain}" == "${whitelist}" ]]; then
if [[ "${listType}" == "whitelist" ]]; then
param="w"
type="white"
elif [[ "${listMain}" == "${regexlist}" && "${wildcard}" == true ]]; then
type="whitelist"
elif [[ "${listType}" == "regex" && "${wildcard}" == true ]]; then
param="-wild"
type="wildcard black"
elif [[ "${listMain}" == "${regexlist}" ]]; then
type="wildcard blacklist"
elif [[ "${listType}" == "regex" ]]; then
param="-regex"
type="regex black"
type="regex filter"
else
param="b"
type="black"
type="blacklist"
fi
echo "Usage: pihole -${param} [options] <domain> <domain2 ...>
Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com'
${type^}list one or more domains
${type^} one or more domains
Options:
-d, --delmode Remove domain(s) from the ${type}list
-nr, --noreload Update ${type}list without refreshing dnsmasq
-d, --delmode Remove domain(s) from the ${type}
-nr, --noreload Update ${type} without reloading the DNS server
-q, --quiet Make output less verbose
-h, --help Show this help dialog
-l, --list Display all your ${type}listed domains
@ -73,7 +72,7 @@ HandleOther() {
# Check validity of domain (don't check for regex entries)
if [[ "${#domain}" -le 253 ]]; then
if [[ "${listMain}" == "${regexlist}" && "${wildcard}" == false ]]; then
if [[ "${listType}" == "regex" && "${wildcard}" == false ]]; then
validDomain="${domain}"
else
validDomain=$(grep -P "^((-|_)*[a-z\\d]((-|_)*[a-z\\d])*(-|_)*)(\\.(-|_)*([a-z\\d]((-|_)*[a-z\\d])*))*$" <<< "${domain}") # Valid chars check
@ -88,175 +87,143 @@ HandleOther() {
fi
}
PoplistFile() {
# Check whitelist file exists, and if not, create it
if [[ ! -f "${whitelist}" ]]; then
touch "${whitelist}"
fi
# Check blacklist file exists, and if not, create it
if [[ ! -f "${blacklist}" ]]; then
touch "${blacklist}"
ProcessDomainList() {
if [[ "${listType}" == "regex" ]]; then
# Regex filter list
listname="regex filters"
else
# Whitelist / Blacklist
listname="${listType}"
fi
for dom in "${domList[@]}"; do
# Logic: If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other
# Format domain into regex filter if requested
if [[ "${wildcard}" == true ]]; then
dom="(^|\\.)${dom//\./\\.}$"
fi
# Logic: If addmode then add to desired list and remove from the other;
# if delmode then remove from desired list but do not add to the other
if ${addmode}; then
AddDomain "${dom}" "${listMain}"
RemoveDomain "${dom}" "${listAlt}"
AddDomain "${dom}" "${listType}"
if [[ ! "${listType}" == "regex" ]]; then
RemoveDomain "${dom}" "${listAlt}"
fi
else
RemoveDomain "${dom}" "${listMain}"
RemoveDomain "${dom}" "${listType}"
fi
done
}
AddDomain() {
local domain list num
# Use printf to escape domain. %q prints the argument in a form that can be reused as shell input
domain="$1"
list="$2"
domain=$(EscapeRegexp "$1")
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
bool=true
# Is the domain in the list we want to add it to?
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then
# Domain not found in the whitelist file, add it!
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Adding ${1} to ${listname}..."
fi
reload=true
# Add it to the list we want to add it to
echo "$1" >> "${list}"
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!"
fi
fi
elif [[ "${list}" == "${regexlist}" ]]; then
[[ -z "${type}" ]] && type="--wildcard-only"
bool=true
domain="${1}"
[[ "${wildcard}" == true ]] && domain="(^|\\.)${domain//\./\\.}$"
# Is the domain in the list we want to add it to?
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM ${list} WHERE domain = '${domain}';")"
# Is the domain in the list?
# Search only for exactly matching lines
grep -Fx "${domain}" "${regexlist}" > /dev/null 2>&1 || bool=false
if [[ "${num}" -ne 0 ]]; then
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!"
fi
return
fi
if [[ "${bool}" == false ]]; then
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Adding ${domain} to regex list..."
fi
reload="restart"
echo "$domain" >> "${regexlist}"
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${domain} already exists in regex list, no need to add!"
fi
fi
# Domain not found in the table, add it!
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Adding ${1} to the ${listname}..."
fi
reload=true
# Insert only the domain here. The enabled and date_added fields will be filled
# with their default values (enabled = true, date_added = current timestamp)
sqlite3 "${gravityDBfile}" "INSERT INTO ${list} (domain) VALUES ('${domain}');"
}
RemoveDomain() {
local domain list num
# Use printf to escape domain. %q prints the argument in a form that can be reused as shell input
domain="$1"
list="$2"
domain=$(EscapeRegexp "$1")
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
bool=true
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
# Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo -e " ${INFO} Removing $1 from ${listname}..."
# /I flag: search case-insensitive
sed -i "/${domain}/Id" "${list}"
reload=true
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} does not exist in ${listname}, no need to remove!"
fi
fi
elif [[ "${list}" == "${regexlist}" ]]; then
[[ -z "${type}" ]] && type="--wildcard-only"
domain="${1}"
[[ "${wildcard}" == true ]] && domain="(^|\\.)${domain//\./\\.}$"
bool=true
# Is it in the list?
grep -Fx "${domain}" "${regexlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then
# Remove it from the other one
echo -e " ${INFO} Removing $domain from regex list..."
local lineNumber
lineNumber=$(grep -Fnx "$domain" "${list}" | cut -f1 -d:)
sed -i "${lineNumber}d" "${list}"
reload=true
else
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${domain} does not exist in regex list, no need to remove!"
fi
fi
# Is the domain in the list we want to remove it from?
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM ${list} WHERE domain = '${domain}';")"
if [[ "${num}" -eq 0 ]]; then
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} does not exist in ${list}, no need to remove!"
fi
return
fi
}
# Update Gravity
Reload() {
echo ""
pihole -g --skip-download "${type:-}"
# Domain found in the table, remove it!
if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} Removing ${1} from the ${listname}..."
fi
reload=true
# Remove it from the current list
sqlite3 "${gravityDBfile}" "DELETE FROM ${list} WHERE domain = '${domain}';"
}
Displaylist() {
if [[ -f ${listMain} ]]; then
if [[ "${listMain}" == "${whitelist}" ]]; then
string="gravity resistant domains"
else
string="domains caught in the sinkhole"
fi
verbose=false
echo -e "Displaying $string:\n"
local list listname count num_pipes domain enabled status nicedate
listname="${listType}"
data="$(sqlite3 "${gravityDBfile}" "SELECT domain,enabled,date_modified FROM ${listType};" 2> /dev/null)"
if [[ -z $data ]]; then
echo -e "Not showing empty ${listname}"
else
echo -e "Displaying ${listname}:"
count=1
while IFS= read -r RD || [ -n "${RD}" ]; do
echo " ${count}: ${RD}"
while IFS= read -r line
do
# Count number of pipes seen in this line
# This is necessary because we can only detect the pipe separating the fields
# from the end backwards as the domain (which is the first field) may contain
# pipe symbols as they are perfectly valid regex filter control characters
num_pipes="$(grep -c "^" <<< "$(grep -o "|" <<< "${line}")")"
# Extract domain and enabled status based on the obtained number of pipe characters
domain="$(cut -d'|' -f"-$((num_pipes-1))" <<< "${line}")"
enabled="$(cut -d'|' -f"$((num_pipes))" <<< "${line}")"
datemod="$(cut -d'|' -f"$((num_pipes+1))" <<< "${line}")"
# Translate boolean status into human readable string
if [[ "${enabled}" -eq 1 ]]; then
status="enabled"
else
status="disabled"
fi
# Get nice representation of numerical date stored in database
nicedate=$(date --rfc-2822 -d "@${datemod}")
echo " ${count}: ${domain} (${status}, last modified ${nicedate})"
count=$((count+1))
done < "${listMain}"
else
echo -e " ${COL_LIGHT_RED}${listMain} does not exist!${COL_NC}"
done <<< "${data}"
fi
exit 0;
}
NukeList() {
if [[ -f "${listMain}" ]]; then
# Back up original list
cp "${listMain}" "${listMain}.bck~"
# Empty out file
echo "" > "${listMain}"
fi
sqlite3 "${gravityDBfile}" "DELETE FROM ${listType};"
}
for var in "$@"; do
case "${var}" in
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
"-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";;
"--wild" | "wildcard" ) listMain="${regexlist}"; wildcard=true;;
"--regex" | "regex" ) listMain="${regexlist}";;
"-w" | "whitelist" ) listType="whitelist"; listAlt="blacklist";;
"-b" | "blacklist" ) listType="blacklist"; listAlt="whitelist";;
"--wild" | "wildcard" ) listType="regex"; wildcard=true;;
"--regex" | "regex" ) listType="regex";;
"-nr"| "--noreload" ) reload=false;;
"-d" | "--delmode" ) addmode=false;;
"-q" | "--quiet" ) verbose=false;;
"-h" | "--help" ) helpFunc;;
"-l" | "--list" ) Displaylist;;
"--nuke" ) NukeList;;
"--web" ) web=true;;
* ) HandleOther "${var}";;
esac
done
@ -267,9 +234,13 @@ if [[ $# = 0 ]]; then
helpFunc
fi
PoplistFile
ProcessDomainList
# Used on web interface
if $web; then
echo "DONE"
fi
if [[ "${reload}" != false ]]; then
# Ensure that "restart" is used for Wildcard updates
Reload "${reload}"
pihole restartdns reload
fi

@ -90,6 +90,7 @@ checkout() {
local path
path="development/${binary}"
echo "development" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
elif [[ "${1}" == "master" ]] ; then
# Shortcut to check out master branches
echo -e " ${INFO} Shortcut \"master\" detected - checking out master branches..."
@ -104,6 +105,7 @@ checkout() {
local path
path="master/${binary}"
echo "master" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
elif [[ "${1}" == "core" ]] ; then
str="Fetching branches from ${piholeGitUrl}"
echo -ne " ${INFO} $str"
@ -166,6 +168,7 @@ checkout() {
if check_download_exists "$path"; then
echo " ${TICK} Branch ${2} exists"
echo "${2}" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
FTLinstall "${binary}"
restart_service pihole-FTL
enable_service pihole-FTL

@ -39,8 +39,9 @@ if [[ "$@" == *"once"* ]]; then
# 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)
cp /var/log/pihole.log /var/log/pihole.log.1
cp -p /var/log/pihole.log /var/log/pihole.log.1
echo " " > /var/log/pihole.log
chmod 644 /var/log/pihole.log
fi
else
# Manual flushing
@ -53,6 +54,7 @@ else
echo " " > /var/log/pihole.log
if [ -f /var/log/pihole.log.1 ]; then
echo " " > /var/log/pihole.log.1
chmod 644 /var/log/pihole.log.1
fi
fi
# Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history)

@ -11,7 +11,7 @@
# Globals
piholeDir="/etc/pihole"
adListsList="$piholeDir/adlists.list"
gravityDBfile="${piholeDir}/gravity.db"
wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
options="$*"
adlist=""
@ -73,11 +73,6 @@ Options:
exit 0
fi
if [[ ! -e "$adListsList" ]]; then
echo -e "${COL_LIGHT_RED}The file $adListsList was not found${COL_NC}"
exit 1
fi
# Handle valid options
if [[ "${options}" == *"-bp"* ]]; then
exact="exact"; blockpage=true
@ -186,11 +181,8 @@ fi
# Get adlist file content as array
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
for adlistUrl in $(< "${adListsList}"); do
if [[ "${adlistUrl:0:4}" =~ (http|www.) ]]; then
adlists+=("${adlistUrl}")
fi
done
# Retrieve source URLs from gravity database
mapfile -t adlists <<< "$(sqlite3 "${gravityDBfile}" "SELECT address FROM vw_adlists;" 2> /dev/null)"
fi
# Print "Exact matches for" title

@ -51,6 +51,7 @@ if [[ "$2" == "remote" ]]; then
GITHUB_CORE_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/pi-hole/releases/latest' 2> /dev/null)")"
echo -n "${GITHUB_CORE_VERSION}" > "${GITHUB_VERSION_FILE}"
chmod 644 "${GITHUB_VERSION_FILE}"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
GITHUB_WEB_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/AdminLTE/releases/latest' 2> /dev/null)")"
@ -66,6 +67,7 @@ else
CORE_BRANCH="$(get_local_branch /etc/.pihole)"
echo -n "${CORE_BRANCH}" > "${LOCAL_BRANCH_FILE}"
chmod 644 "${LOCAL_BRANCH_FILE}"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
WEB_BRANCH="$(get_local_branch /var/www/html/admin)"
@ -79,6 +81,7 @@ else
CORE_VERSION="$(get_local_version /etc/.pihole)"
echo -n "${CORE_VERSION}" > "${LOCAL_VERSION_FILE}"
chmod 644 "${LOCAL_VERSION_FILE}"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
WEB_VERSION="$(get_local_version /var/www/html/admin)"

@ -17,6 +17,8 @@ readonly FTLconf="/etc/pihole/pihole-FTL.conf"
# 03 -> wildcards
readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf"
readonly gravityDBfile="/etc/pihole/gravity.db"
coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then
source ${coltable}
@ -322,6 +324,7 @@ dhcp-option=option:router,${DHCP_ROUTER}
dhcp-leasefile=/etc/pihole/dhcp.leases
#quiet-dhcp
" > "${dhcpconfig}"
chmod 644 "${dhcpconfig}"
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
@ -385,19 +388,17 @@ SetWebUILayout() {
}
CustomizeAdLists() {
list="/etc/pihole/adlists.list"
local address
address="${args[3]}"
if [[ "${args[2]}" == "enable" ]]; then
sed -i "\\@${args[3]}@s/^#http/http/g" "${list}"
sqlite3 "${gravityDBfile}" "UPDATE adlists SET enabled = 1 WHERE address = '${address}'"
elif [[ "${args[2]}" == "disable" ]]; then
sed -i "\\@${args[3]}@s/^http/#http/g" "${list}"
sqlite3 "${gravityDBfile}" "UPDATE adlists SET enabled = 0 WHERE address = '${address}'"
elif [[ "${args[2]}" == "add" ]]; then
if [[ $(grep -c "^${args[3]}$" "${list}") -eq 0 ]] ; then
echo "${args[3]}" >> ${list}
fi
sqlite3 "${gravityDBfile}" "INSERT OR IGNORE INTO adlists (address) VALUES ('${address}')"
elif [[ "${args[2]}" == "del" ]]; then
var=$(echo "${args[3]}" | sed 's/\//\\\//g')
sed -i "/${var}/Id" "${list}"
sqlite3 "${gravityDBfile}" "DELETE FROM adlists WHERE address = '${address}'"
else
echo "Not permitted"
return 1
@ -541,11 +542,13 @@ addAudit()
do
echo "${var}" >> /etc/pihole/auditlog.list
done
chmod 644 /etc/pihole/auditlog.list
}
clearAudit()
{
echo -n "" > /etc/pihole/auditlog.list
chmod 644 /etc/pihole/auditlog.list
}
SetPrivacyLevel() {

@ -0,0 +1,92 @@
CREATE TABLE whitelist
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
CREATE TABLE blacklist
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
CREATE TABLE regex
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
domain TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
CREATE TABLE adlists
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
address TEXT UNIQUE NOT NULL,
enabled BOOLEAN NOT NULL DEFAULT 1,
date_added INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
date_modified INTEGER NOT NULL DEFAULT (cast(strftime('%s', 'now') as int)),
comment TEXT
);
CREATE TABLE gravity
(
domain TEXT PRIMARY KEY
);
CREATE TABLE info
(
property TEXT PRIMARY KEY,
value TEXT NOT NULL
);
INSERT INTO info VALUES("version","1");
CREATE VIEW vw_gravity AS SELECT a.domain
FROM gravity a
WHERE a.domain NOT IN (SELECT domain from whitelist WHERE enabled == 1);
CREATE VIEW vw_whitelist AS SELECT a.domain
FROM whitelist a
WHERE a.enabled == 1
ORDER BY a.id;
CREATE TRIGGER tr_whitelist_update AFTER UPDATE ON whitelist
BEGIN
UPDATE whitelist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
CREATE VIEW vw_blacklist AS SELECT a.domain
FROM blacklist a
WHERE a.enabled == 1 AND a.domain NOT IN vw_whitelist
ORDER BY a.id;
CREATE TRIGGER tr_blacklist_update AFTER UPDATE ON blacklist
BEGIN
UPDATE blacklist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
CREATE VIEW vw_regex AS SELECT a.domain
FROM regex a
WHERE a.enabled == 1
ORDER BY a.id;
CREATE TRIGGER tr_regex_update AFTER UPDATE ON regex
BEGIN
UPDATE regex SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END;
CREATE VIEW vw_adlists AS SELECT a.address
FROM adlists a
WHERE a.enabled == 1
ORDER BY a.id;
CREATE TRIGGER tr_adlists_update AFTER UPDATE ON adlists
BEGIN
UPDATE adlists SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE address = NEW.address;
END;

@ -118,7 +118,7 @@ done
# If the color table file exists,
if [[ -f "${coltable}" ]]; then
# source it
source ${coltable}
source "${coltable}"
# Otherwise,
else
# Set these values so the installer can still run in color
@ -182,26 +182,26 @@ if is_command apt-get ; then
# A variable to store the command used to update the package cache
UPDATE_PKG_CACHE="${PKG_MANAGER} update"
# An array for something...
PKG_INSTALL=(${PKG_MANAGER} --yes --no-install-recommends install)
PKG_INSTALL=("${PKG_MANAGER}" --yes --no-install-recommends install)
# grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE
PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true"
# Some distros vary slightly so these fixes for dependencies may apply
# on Ubuntu 18.04.1 LTS we need to add the universe repository to gain access to dialog and dhcpcd5
APT_SOURCES="/etc/apt/sources.list"
if awk 'BEGIN{a=1;b=0}/bionic main/{a=0}/bionic.*universe/{b=1}END{exit a + b}' ${APT_SOURCES}; then
if ! whiptail --defaultno --title "Dependencies Require Update to Allowed Repositories" --yesno "Would you like to enable 'universe' repository?\\n\\nThis repository is required by the following packages:\\n\\n- dhcpcd5\\n- dialog" ${r} ${c}; then
if ! whiptail --defaultno --title "Dependencies Require Update to Allowed Repositories" --yesno "Would you like to enable 'universe' repository?\\n\\nThis repository is required by the following packages:\\n\\n- dhcpcd5\\n- dialog" "${r}" "${c}"; then
printf " %b Aborting installation: dependencies could not be installed.\\n" "${CROSS}"
exit # exit the installer
else
printf " %b Enabling universe package repository for Ubuntu Bionic\\n" "${INFO}"
cp ${APT_SOURCES} ${APT_SOURCES}.backup # Backup current repo list
cp -p ${APT_SOURCES} ${APT_SOURCES}.backup # Backup current repo list
printf " %b Backed up current configuration to %s\\n" "${TICK}" "${APT_SOURCES}.backup"
add-apt-repository universe
printf " %b Enabled %s\\n" "${TICK}" "'universe' repository"
fi
fi
# Debian 7 doesn't have iproute2 so if the dry run install is successful,
if ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1; then
if "${PKG_MANAGER}" install --dry-run iproute2 > /dev/null 2>&1; then
# we can install it
iproute_pkg="iproute2"
# Otherwise,
@ -222,7 +222,7 @@ if is_command apt-get ; then
# Check if installed php is v 7.0, or newer to determine packages to install
if [[ "$phpInsNewer" != true ]]; then
# Prefer the php metapackage if it's there
if ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1; then
if "${PKG_MANAGER}" install --dry-run php > /dev/null 2>&1; then
phpVer="php"
# fall back on the php5 packages
else
@ -233,19 +233,19 @@ if is_command apt-get ; then
phpVer="php$phpInsMajor.$phpInsMinor"
fi
# We also need the correct version for `php-sqlite` (which differs across distros)
if ${PKG_MANAGER} install --dry-run ${phpVer}-sqlite3 > /dev/null 2>&1; then
if "${PKG_MANAGER}" install --dry-run "${phpVer}-sqlite3" > /dev/null 2>&1; then
phpSqlite="sqlite3"
else
phpSqlite="sqlite"
fi
# Since our install script is so large, we need several other programs to successfully get a machine provisioned
# These programs are stored in an array so they can be looped through later
INSTALLER_DEPS=(apt-utils dialog debconf dhcpcd5 git ${iproute_pkg} whiptail)
INSTALLER_DEPS=(apt-utils dialog debconf dhcpcd5 git "${iproute_pkg}" whiptail)
# Pi-hole itself has several dependencies that also need to be installed
PIHOLE_DEPS=(cron curl dnsutils iputils-ping lsof netcat psmisc sudo unzip wget idn2 sqlite3 libcap2-bin dns-root-data resolvconf libcap2)
# The Web dashboard has some that also need to be installed
# It's useful to separate the two since our repos are also setup as "Core" code and "Web" code
PIHOLE_WEB_DEPS=(lighttpd ${phpVer}-common ${phpVer}-cgi ${phpVer}-${phpSqlite})
PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-${phpSqlite}")
# The Web server user,
LIGHTTPD_USER="www-data"
# group,
@ -281,7 +281,7 @@ elif is_command rpm ; then
# Fedora and family update cache on every PKG_INSTALL call, no need for a separate update.
UPDATE_PKG_CACHE=":"
PKG_INSTALL=(${PKG_MANAGER} install -y)
PKG_INSTALL=("${PKG_MANAGER}" install -y)
PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l"
INSTALLER_DEPS=(dialog git iproute newt procps-ng which)
PIHOLE_DEPS=(bind-utils cronie curl findutils nmap-ncat sudo unzip wget libidn2 psmisc sqlite libcap)
@ -319,7 +319,7 @@ elif is_command rpm ; then
# The default php on CentOS 7.x is 5.4 which is EOL
# Check if the version of PHP available via installed repositories is >= to PHP 7
AVAILABLE_PHP_VERSION=$(${PKG_MANAGER} info php | grep -i version | grep -o '[0-9]\+' | head -1)
AVAILABLE_PHP_VERSION=$("${PKG_MANAGER}" info php | grep -i version | grep -o '[0-9]\+' | head -1)
if [[ $AVAILABLE_PHP_VERSION -ge $SUPPORTED_CENTOS_PHP_VERSION ]]; then
# Since PHP 7 is available by default, install via default PHP package names
: # do nothing as PHP is current
@ -329,7 +329,7 @@ elif is_command rpm ; then
rpm -q ${REMI_PKG} &> /dev/null || rc=$?
if [[ $rc -ne 0 ]]; then
# The PHP version available via default repositories is older than version 7
if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information" ${r} ${c}; then
if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information" "${r}" "${c}"; then
# User decided to NOT update PHP from REMI, attempt to install the default available PHP version
printf " %b User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use.\\n" "${INFO}"
: # continue with unsupported php version
@ -352,7 +352,7 @@ elif is_command rpm ; then
fi
else
# Warn user of unsupported version of Fedora or CentOS
if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+" ${r} ${c}; then
if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+" "${r}" "${c}"; then
printf " %b Aborting installation due to unsupported RPM based distribution\\n" "${CROSS}"
exit # exit the installer
else
@ -414,6 +414,9 @@ make_repo() {
fi
# Clone the repo and return the return code from this command
git clone -q --depth 20 "${remoteRepo}" "${directory}" &> /dev/null || return $?
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# Show a colored message showing it's status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Always return 0? Not sure this is correct
@ -447,6 +450,8 @@ update_repo() {
git pull --quiet &> /dev/null || return $?
# Show a completion message
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# Move back into the original directory
cd "${curdir}" &> /dev/null || return 1
return 0
@ -494,6 +499,8 @@ resetRepo() {
printf " %b %s..." "${INFO}" "${str}"
# Use git to remove the local changes
git reset --hard &> /dev/null || return $?
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
# And show the status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Returning success anyway?
@ -537,15 +544,15 @@ get_available_interfaces() {
# A function for displaying the dialogs the user sees when first running the installer
welcomeDialogs() {
# Display the welcome dialog using an appropriately sized window via the calculation conducted earlier in the script
whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\\n\\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c}
whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\\n\\nThis installer will transform your device into a network-wide ad blocker!" "${r}" "${c}"
# Request that users donate if they enjoy the software since we all work on it in our free time
whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\\n\\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c}
whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\\n\\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" "${r}" "${c}"
# Explain the need for a static address
whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly.
In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c}
In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." "${r}" "${c}"
}
# We need to make sure there is enough space before installing, so there is a function to check this
@ -632,7 +639,7 @@ chooseInterface() {
# Feed the available interfaces into this while loop
done <<< "${availableInterfaces}"
# The whiptail command that will be run, stored in a variable
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount})
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" "${r}" "${c}" "${interfaceCount}")
# Now run the command using the interfaces saved into the array
chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \
# If the user chooses Cancel, exit
@ -713,7 +720,7 @@ useIPv6dialog() {
# If the IPV6_ADDRESS contains a value
if [[ ! -z "${IPV6_ADDRESS}" ]]; then
# Display that IPv6 is supported and will be used
whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c}
whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." "${r}" "${c}"
fi
}
@ -723,7 +730,7 @@ use4andor6() {
local useIPv4
local useIPv6
# Let use select IPv4 and/or IPv6 via a checklist
cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2)
cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" "${r}" "${c}" 2)
# In an array, show the options available:
# IPv4 (on by default)
options=(IPv4 "Block ads over IPv4" on
@ -772,11 +779,11 @@ getStaticIPv4Settings() {
# This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address?
IP address: ${IPV4_ADDRESS}
Gateway: ${IPv4gw}" ${r} ${c}; then
Gateway: ${IPv4gw}" "${r}" "${c}"; then
# If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict.
whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that.
If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want.
It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c}
It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." "${r}" "${c}"
# Nothing else to do since the variables are already set above
else
# Otherwise, we need to ask the user to input their desired settings.
@ -785,13 +792,13 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
until [[ "${ipSettingsCorrect}" = True ]]; do
# Ask for the IPv4 address
IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \
IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" "${r}" "${c}" "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \
# Cancelling IPv4 settings window
{ ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}"
# Ask for the gateway
IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) || \
IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" "${r}" "${c}" "${IPv4gw}" 3>&1 1>&2 2>&3) || \
# Cancelling gateway settings window
{ ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}"
@ -799,7 +806,7 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
# Give the user a chance to review their settings before moving on
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct?
IP address: ${IPV4_ADDRESS}
Gateway: ${IPv4gw}" ${r} ${c}; then
Gateway: ${IPv4gw}" "${r}" "${c}"; then
# After that's done, the loop ends and we move on
ipSettingsCorrect=True
else
@ -847,7 +854,7 @@ setIFCFG() {
# Put the IP in variables without the CIDR notation
printf -v CIDR "%s" "${IPV4_ADDRESS##*/}"
# Backup existing interface configuration:
cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig
cp -p "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig
# Build Interface configuration file using the GLOBAL variables we have
{
echo "# Configured via Pi-hole installer"
@ -861,6 +868,8 @@ setIFCFG() {
echo "DNS2=$PIHOLE_DNS_2"
echo "USERCTL=no"
}> "${IFCFG_FILE}"
chmod 644 "${IFCFG_FILE}"
chown root:root "${IFCFG_FILE}"
# Use ip to immediately set the new address
ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}"
# If NetworkMangler command line interface exists and ready to mangle,
@ -925,7 +934,7 @@ valid_ip() {
# and set the new one to a dot (period)
IFS='.'
# Put the IP into an array
ip=(${ip})
ip=("${ip}")
# Restore the IFS to what it was
IFS=${OIFS}
## Evaluate each octet by checking if it's less than or equal to 255 (the max for each octet)
@ -935,7 +944,7 @@ valid_ip() {
stat=$?
fi
# Return the exit code
return ${stat}
return "${stat}"
}
# A function to choose the upstream DNS provider(s)
@ -965,7 +974,7 @@ setDNS() {
# Restore the IFS to what it was
IFS=${OIFS}
# In a whiptail dialog, show the options
DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 7 \
DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." "${r}" "${c}" 7 \
"${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \
# exit if Cancel is selected
{ printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
@ -995,7 +1004,7 @@ setDNS() {
fi
# Dialog for the user to enter custom upstream servers
piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) || \
piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'" "${r}" "${c}" "${prePopulate}" 3>&1 1>&2 2>&3) || \
{ printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
# Clean user input and replace whitespace with comma.
piholeDNS=$(sed 's/[, \t]\+/,/g' <<< "${piholeDNS}")
@ -1028,7 +1037,7 @@ setDNS() {
# Otherwise,
else
# Show the settings
if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then
if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}" "${r}" "${c}"); then
# and break from the loop since the servers are valid
DNSSettingsCorrect=True
# Otherwise,
@ -1119,7 +1128,7 @@ setAdminFlag() {
local WebChoices
# Similar to the logging function, ask what the user wants
WebToggleCommand=(whiptail --separate-output --radiolist "Do you wish to install the web admin interface?" ${r} ${c} 6)
WebToggleCommand=(whiptail --separate-output --radiolist "Do you wish to install the web admin interface?" "${r}" "${c}" 6)
# with the default being enabled
WebChooseOptions=("On (Recommended)" "" on
Off "" off)
@ -1185,6 +1194,7 @@ chooseBlocklists() {
do
appendToListsFile "${choice}"
done
chmod 644 "${adlistFile}"
}
# Accept a string parameter, it must be one of the default lists
@ -1225,6 +1235,7 @@ version_check_dnsmasq() {
local dnsmasq_conf="/etc/dnsmasq.conf"
local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig"
local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list"
local dnsmasq_pihole_id_string2="# Dnsmasq config for Pi-hole's FTLDNS"
local dnsmasq_original_config="${PI_HOLE_LOCAL_REPO}/advanced/dnsmasq.conf.original"
local dnsmasq_pihole_01_snippet="${PI_HOLE_LOCAL_REPO}/advanced/01-pihole.conf"
local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf"
@ -1232,16 +1243,17 @@ version_check_dnsmasq() {
# If the dnsmasq config file exists
if [[ -f "${dnsmasq_conf}" ]]; then
printf " %b Existing dnsmasq.conf found..." "${INFO}"
# If gravity.list is found within this file, we presume it's from older versions on Pi-hole,
if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then
# If a specific string is found within this file, we presume it's from older versions on Pi-hole,
if grep -q "${dnsmasq_pihole_id_string}" "${dnsmasq_conf}" ||
grep -q "${dnsmasq_pihole_id_string2}" "${dnsmasq_conf}"; then
printf " it is from a previous Pi-hole install.\\n"
printf " %b Backing up dnsmasq.conf to dnsmasq.conf.orig..." "${INFO}"
# so backup the original file
mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig}
mv -f "${dnsmasq_conf}" "${dnsmasq_conf_orig}"
printf "%b %b Backing up dnsmasq.conf to dnsmasq.conf.orig...\\n" "${OVER}" "${TICK}"
printf " %b Restoring default dnsmasq.conf..." "${INFO}"
# and replace it with the default
cp ${dnsmasq_original_config} ${dnsmasq_conf}
install -D -m 644 -T "${dnsmasq_original_config}" "${dnsmasq_conf}"
printf "%b %b Restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}"
# Otherwise,
else
@ -1252,47 +1264,47 @@ version_check_dnsmasq() {
# If a file cannot be found,
printf " %b No dnsmasq.conf found... restoring default dnsmasq.conf..." "${INFO}"
# restore the default one
cp ${dnsmasq_original_config} ${dnsmasq_conf}
install -D -m 644 -T "${dnsmasq_original_config}" "${dnsmasq_conf}"
printf "%b %b No dnsmasq.conf found... restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}"
fi
printf " %b Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." "${INFO}"
# Check to see if dnsmasq directory exists (it may not due to being a fresh install and dnsmasq no longer being a dependency)
if [[ ! -d "/etc/dnsmasq.d" ]];then
mkdir "/etc/dnsmasq.d"
install -d -m 755 "/etc/dnsmasq.d"
fi
# Copy the new Pi-hole DNS config file into the dnsmasq.d directory
cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location}
install -D -m 644 -T "${dnsmasq_pihole_01_snippet}" "${dnsmasq_pihole_01_location}"
printf "%b %b Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf\\n" "${OVER}" "${TICK}"
# Replace our placeholder values with the GLOBAL DNS variables that we populated earlier
# First, swap in the interface to listen on
sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location}
sed -i "s/@INT@/$PIHOLE_INTERFACE/" "${dnsmasq_pihole_01_location}"
if [[ "${PIHOLE_DNS_1}" != "" ]]; then
# Then swap in the primary DNS server
sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location}
sed -i "s/@DNS1@/$PIHOLE_DNS_1/" "${dnsmasq_pihole_01_location}"
else
#
sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location}
sed -i '/^server=@DNS1@/d' "${dnsmasq_pihole_01_location}"
fi
if [[ "${PIHOLE_DNS_2}" != "" ]]; then
# Then swap in the primary DNS server
sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location}
sed -i "s/@DNS2@/$PIHOLE_DNS_2/" "${dnsmasq_pihole_01_location}"
else
#
sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location}
sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_location}"
fi
#
sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf}
sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' "${dnsmasq_conf}"
# If the user does not want to enable logging,
if [[ "${QUERY_LOGGING}" == false ]] ; then
# Disable it by commenting out the directive in the DNS config file
sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location}
sed -i 's/^log-queries/#log-queries/' "${dnsmasq_pihole_01_location}"
# Otherwise,
else
# enable it by uncommenting the directive in the DNS config file
sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location}
sed -i 's/^#log-queries/log-queries/' "${dnsmasq_pihole_01_location}"
fi
}
@ -1360,6 +1372,7 @@ installConfigs() {
# Format: Name;Primary IPv4;Secondary IPv4;Primary IPv6;Secondary IPv6
# Some values may be empty (for example: DNS servers without IPv6 support)
echo "${DNS_SERVERS}" > "${PI_HOLE_CONFIG_DIR}/dns-servers.conf"
chmod 644 "${PI_HOLE_CONFIG_DIR}/dns-servers.conf"
# Install empty file if it does not exist
if [[ ! -r "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then
@ -1378,19 +1391,18 @@ installConfigs() {
if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
# and if the Web server conf directory does not exist,
if [[ ! -d "/etc/lighttpd" ]]; then
# make it
mkdir /etc/lighttpd
# and set the owners
chown "${USER}":root /etc/lighttpd
# make it and set the owners
install -d -m 755 -o "${USER}" -g root /etc/lighttpd
# Otherwise, if the config file already exists
elif [[ -f "/etc/lighttpd/lighttpd.conf" ]]; then
# back up the original
mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig
fi
# and copy in the config file Pi-hole needs
cp ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf
install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf
# Make sure the external.conf file exists, as lighttpd v1.4.50 crashes without it
touch /etc/lighttpd/external.conf
chmod 644 /etc/lighttpd/external.conf
# if there is a custom block page in the html/pihole directory, replace 404 handler in lighttpd config
if [[ -f "${PI_HOLE_BLOCKPAGE_DIR}/custom.php" ]]; then
sed -i 's/^\(server\.error-handler-404\s*=\s*\).*$/\1"pihole\/custom\.php"/' /etc/lighttpd/lighttpd.conf
@ -1421,16 +1433,16 @@ install_manpage() {
fi
if [[ ! -d "/usr/local/share/man/man8" ]]; then
# if not present, create man8 directory
mkdir /usr/local/share/man/man8
install -d -m 755 /usr/local/share/man/man8
fi
if [[ ! -d "/usr/local/share/man/man5" ]]; then
# if not present, create man8 directory
mkdir /usr/local/share/man/man5
# if not present, create man5 directory
install -d -m 755 /usr/local/share/man/man5
fi
# Testing complete, copy the files & update the man db
cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole.8 /usr/local/share/man/man8/pihole.8
cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.8 /usr/local/share/man/man8/pihole-FTL.8
cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.conf.5 /usr/local/share/man/man5/pihole-FTL.conf.5
install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/manpages/pihole.8 /usr/local/share/man/man8/pihole.8
install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.8 /usr/local/share/man/man8/pihole-FTL.8
install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.conf.5 /usr/local/share/man/man5/pihole-FTL.conf.5
if mandb -q &>/dev/null; then
# Updated successfully
printf "%b %b man pages installed and database updated\\n" "${OVER}" "${TICK}"
@ -1605,7 +1617,6 @@ install_dependent_packages() {
# Install packages passed in via argument array
# No spinner - conflicts with set -e
declare -a argArray1=("${!1}")
declare -a installArray
# Debian based package install - debconf will download the entire package list
@ -1615,7 +1626,7 @@ install_dependent_packages() {
# installed by us, and remove only the installed packages, and not the entire list.
if is_command debconf-apt-progress ; then
# For each package,
for i in "${argArray1[@]}"; do
for i in "$@"; do
printf " %b Checking for %s..." "${INFO}" "${i}"
if dpkg-query -W -f='${Status}' "${i}" 2>/dev/null | grep "ok installed" &> /dev/null; then
printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}"
@ -1634,9 +1645,9 @@ install_dependent_packages() {
fi
# Install Fedora/CentOS packages
for i in "${argArray1[@]}"; do
for i in "$@"; do
printf " %b Checking for %s..." "${INFO}" "${i}"
if ${PKG_MANAGER} -q list installed "${i}" &> /dev/null; then
if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then
printf "%b %b Checking for %s" "${OVER}" "${TICK}" "${i}"
else
printf "%b %b Checking for %s (will be installed)" "${OVER}" "${INFO}" "${i}"
@ -1660,7 +1671,7 @@ installPiholeWeb() {
# Install the directory
install -d -m 0755 ${PI_HOLE_BLOCKPAGE_DIR}
# and the blockpage
install -D ${PI_HOLE_LOCAL_REPO}/advanced/{index,blockingpage}.* ${PI_HOLE_BLOCKPAGE_DIR}/
install -D -m 644 ${PI_HOLE_LOCAL_REPO}/advanced/{index,blockingpage}.* ${PI_HOLE_BLOCKPAGE_DIR}/
# Remove superseded file
if [[ -e "${PI_HOLE_BLOCKPAGE_DIR}/index.js" ]]; then
@ -1687,7 +1698,7 @@ installPiholeWeb() {
local str="Installing sudoer file"
printf "\\n %b %s..." "${INFO}" "${str}"
# Make the .d directory if it doesn't exist
mkdir -p /etc/sudoers.d/
install -d -m 755 /etc/sudoers.d/
# and copy in the pihole sudoers file
install -m 0640 ${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole.sudo /etc/sudoers.d/pihole
# Add lighttpd user (OS dependent) to sudoers file
@ -1710,7 +1721,8 @@ installCron() {
local str="Installing latest Cron script"
printf "\\n %b %s..." "${INFO}" "${str}"
# Copy the cron file over from the local repo
cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole.cron /etc/cron.d/pihole
# File must not be world or group writeable and must be owned by root
install -D -m 644 -T -o root -g root ${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole.cron /etc/cron.d/pihole
# Randomize gravity update time
sed -i "s/59 1 /$((1 + RANDOM % 58)) $((3 + RANDOM % 2))/" /etc/cron.d/pihole
# Randomize update checker time
@ -1753,7 +1765,7 @@ configureFirewall() {
# If a firewall is running,
if firewall-cmd --state &> /dev/null; then
# ask if the user wants to install Pi-hole's default firewall rules
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" ${r} ${c} || \
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" "${r}" "${c}" || \
{ printf " %b Not installing firewall rulesets.\\n" "${INFO}"; return 0; }
printf " %b Configuring FirewallD for httpd and pihole-FTL\\n" "${TICK}"
# Allow HTTP and DNS traffic
@ -1766,7 +1778,7 @@ configureFirewall() {
# If chain Policy is not ACCEPT or last Rule is not ACCEPT
# then check and insert our Rules above the DROP/REJECT Rule.
if iptables -S INPUT | head -n1 | grep -qv '^-P.*ACCEPT$' || iptables -S INPUT | tail -n1 | grep -qv '^-\(A\|P\).*ACCEPT$'; then
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" ${r} ${c} || \
whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" "${r}" "${c}" || \
{ printf " %b Not installing firewall rulesets.\\n" "${INFO}"; return 0; }
printf " %b Installing new IPTables firewall rulesets\\n" "${TICK}"
# Check chain first, otherwise a new rule will duplicate old ones
@ -1818,6 +1830,7 @@ finalExports() {
echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}"
echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}"
}>> "${setupVars}"
chmod 644 "${setupVars}"
# Set the privacy level
sed -i '/PRIVACYLEVEL/d' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf"
@ -1840,7 +1853,7 @@ installLogrotate() {
local str="Installing latest logrotate script"
printf "\\n %b %s..." "${INFO}" "${str}"
# Copy the file over from the local repo
cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/logrotate /etc/pihole/logrotate
install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/advanced/Templates/logrotate /etc/pihole/logrotate
# Different operating systems have different user / group
# settings for logrotate that makes it impossible to create
# a static logrotate file that will work with e.g.
@ -1859,29 +1872,26 @@ installLogrotate() {
# At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break.
# Refactoring of install script has changed the name of a couple of variables. Sort them out here.
accountForRefactor() {
sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars}
sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars}
sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars}
sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars}
sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars}
sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars}
sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars}
sed -i 's/^INSTALL_WEB=/INSTALL_WEB_INTERFACE=/' ${setupVars}
sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' "${setupVars}"
sed -i 's/IPv4_address/IPV4_ADDRESS/g' "${setupVars}"
sed -i 's/IPv4addr/IPV4_ADDRESS/g' "${setupVars}"
sed -i 's/IPv6_address/IPV6_ADDRESS/g' "${setupVars}"
sed -i 's/piholeIPv6/IPV6_ADDRESS/g' "${setupVars}"
sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' "${setupVars}"
sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' "${setupVars}"
sed -i 's/^INSTALL_WEB=/INSTALL_WEB_INTERFACE=/' "${setupVars}"
# Add 'INSTALL_WEB_SERVER', if its not been applied already: https://github.com/pi-hole/pi-hole/pull/2115
if ! grep -q '^INSTALL_WEB_SERVER=' ${setupVars}; then
local webserver_installed=false
if grep -q '^INSTALL_WEB_INTERFACE=true' ${setupVars}; then
webserver_installed=true
fi
echo -e "INSTALL_WEB_SERVER=$webserver_installed" >> ${setupVars}
echo -e "INSTALL_WEB_SERVER=$webserver_installed" >> "${setupVars}"
fi
}
# Install base files and web interface
installPihole() {
# Create the pihole user
create_pihole_user
# If the user wants to install the Web interface,
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
if [[ ! -d "${webroot}" ]]; then
@ -1893,6 +1903,9 @@ installPihole() {
# Set the owner and permissions
chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} ${webroot}
chmod 0775 ${webroot}
# Repair permissions if /var/www/html is not world readable
chmod a+rx /var/www
chmod a+rx /var/www/html
# Give pihole access to the Web server group
usermod -a -G ${LIGHTTPD_GROUP} pihole
# If the lighttpd command is executable,
@ -1955,7 +1968,7 @@ checkSelinux() {
# If it's enforcing,
if [[ "${enforceMode}" == "Enforcing" ]]; then
# Explain Pi-hole does not support it yet
whiptail --defaultno --title "SELinux Enforcing Detected" --yesno "SELinux is being ENFORCED on your system! \\n\\nPi-hole currently does not support SELinux, but you may still continue with the installation.\\n\\nNote: Web Admin will not be fully functional unless you set your policies correctly\\n\\nContinue installing Pi-hole?" ${r} ${c} || \
whiptail --defaultno --title "SELinux Enforcing Detected" --yesno "SELinux is being ENFORCED on your system! \\n\\nPi-hole currently does not support SELinux, but you may still continue with the installation.\\n\\nNote: Web Admin will not be fully functional unless you set your policies correctly\\n\\nContinue installing Pi-hole?" "${r}" "${c}" || \
{ printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
printf " %b Continuing installation with SELinux Enforcing\\n" "${INFO}"
printf " %b Please refer to official SELinux documentation to create a custom policy\\n" "${INFO}"
@ -1994,7 +2007,7 @@ If you set a new IP address, you should restart the Pi.
The install log is in /etc/pihole.
${additional}" ${r} ${c}
${additional}" "${r}" "${c}"
}
update_dialogs() {
@ -2015,7 +2028,7 @@ update_dialogs() {
opt2b="This will reset your Pi-hole and allow you to enter new settings."
# Display the information to the user
UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\\n\\nWe have detected an existing install.\\n\\nPlease choose from the following options: \\n($strAdd)" ${r} ${c} 2 \
UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\\n\\nWe have detected an existing install.\\n\\nPlease choose from the following options: \\n($strAdd)" "${r}" "${c}" 2 \
"${opt1a}" "${opt1b}" \
"${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) || \
{ printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
@ -2104,6 +2117,8 @@ checkout_pull_branch() {
printf " %b %s" "${INFO}" "$str"
git checkout "${branch}" --quiet || return 1
printf "%b %b %s\\n" "${OVER}" "${TICK}" "$str"
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}"
git_pull=$(git pull || return 1)
@ -2200,6 +2215,8 @@ FTLinstall() {
# Before stopping FTL, we download the macvendor database
curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true
chmod 644 "${PI_HOLE_CONFIG_DIR}/macvendor.db"
chown pihole:pihole "${PI_HOLE_CONFIG_DIR}/macvendor.db"
# Stop pihole-FTL service if available
stop_service pihole-FTL &> /dev/null
@ -2250,6 +2267,7 @@ disable_dnsmasq() {
fi
# Create /etc/dnsmasq.conf
echo "conf-dir=/etc/dnsmasq.d" > "${conffile}"
chmod 644 "${conffile}"
}
get_binary_name() {
@ -2431,6 +2449,7 @@ copy_to_install_log() {
# Copy the contents of file descriptor 3 into the install log
# Since we use color codes such as '\e[1;33m', they should be removed
sed 's/\[[0-9;]\{1,5\}m//g' < /proc/$$/fd/3 > "${installLogLoc}"
chmod 644 "${installLogLoc}"
}
main() {
@ -2506,7 +2525,7 @@ main() {
notify_package_updates_available
# Install packages used by this installation script
install_dependent_packages INSTALLER_DEPS[@]
install_dependent_packages "${INSTALLER_DEPS[@]}"
# Check if SELinux is Enforcing
checkSelinux
@ -2515,7 +2534,7 @@ main() {
# Display welcome dialogs
welcomeDialogs
# Create directory for Pi-hole storage
mkdir -p /etc/pihole/
install -d -m 755 /etc/pihole/
# Determine available interfaces
get_available_interfaces
# Find interfaces and let the user choose one
@ -2537,7 +2556,7 @@ main() {
installDefaultBlocklists
# Source ${setupVars} to use predefined user variables in the functions
source ${setupVars}
source "${setupVars}"
# Get the privacy level if it exists (default is 0)
if [[ -f "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then
@ -2557,7 +2576,7 @@ main() {
dep_install_list+=("${PIHOLE_WEB_DEPS[@]}")
fi
install_dependent_packages dep_install_list[@]
install_dependent_packages "${dep_install_list[@]}"
unset dep_install_list
# On some systems, lighttpd is not enabled on first install. We need to enable it here if the user
@ -2571,6 +2590,8 @@ main() {
else
LIGHTTPD_ENABLED=false
fi
# Create the pihole user
create_pihole_user
# Check if FTL is installed - do this early on as FTL is a hard dependency for Pi-hole
if ! FTLdetect; then
printf " %b FTL Engine not installed\\n" "${CROSS}"
@ -2592,7 +2613,7 @@ main() {
pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
# shellcheck disable=SC1091
. /opt/pihole/webpage.sh
echo "WEBPASSWORD=$(HashPassword ${pw})" >> ${setupVars}
echo "WEBPASSWORD=$(HashPassword "${pw}")" >> "${setupVars}"
fi
fi

@ -153,7 +153,7 @@ removeNoPurge() {
# Restore Resolved
if [[ -e /etc/systemd/resolved.conf.orig ]]; then
${SUDO} cp /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf
${SUDO} cp -p /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf
systemctl reload-or-restart systemd-resolved
fi

@ -23,31 +23,27 @@ PIHOLE_COMMAND="/usr/local/bin/${basename}"
piholeDir="/etc/${basename}"
adListFile="${piholeDir}/adlists.list"
adListDefault="${piholeDir}/adlists.default"
# Legacy (pre v5.0) list file locations
whitelistFile="${piholeDir}/whitelist.txt"
blacklistFile="${piholeDir}/blacklist.txt"
regexFile="${piholeDir}/regex.list"
adListFile="${piholeDir}/adlists.list"
adList="${piholeDir}/gravity.list"
blackList="${piholeDir}/black.list"
localList="${piholeDir}/local.list"
VPNList="/etc/openvpn/ipp.txt"
piholeGitDir="/etc/.pihole"
gravityDBfile="${piholeDir}/gravity.db"
gravityDBschema="${piholeGitDir}/advanced/Templates/gravity.db.sql"
optimize_database=false
domainsExtension="domains"
matterAndLight="${basename}.0.matterandlight.txt"
parsedMatter="${basename}.1.parsedmatter.txt"
whitelistMatter="${basename}.2.whitelistmatter.txt"
accretionDisc="${basename}.3.accretionDisc.txt"
preEventHorizon="list.preEventHorizon"
skipDownload="false"
resolver="pihole-FTL"
haveSourceUrls=true
# Source setupVars from install script
setupVars="${piholeDir}/setupVars.conf"
if [[ -f "${setupVars}" ]];then
@ -83,17 +79,104 @@ if [[ -r "${piholeDir}/pihole.conf" ]]; then
echo -e " ${COL_LIGHT_RED}Ignoring overrides specified within pihole.conf! ${COL_NC}"
fi
# Determine if Pi-hole blocking is disabled
# If this is the case, we want to update
# gravity.list.bck and black.list.bck instead of
# gravity.list and black.list
detect_pihole_blocking_status() {
if [[ "${BLOCKING_ENABLED}" == false ]]; then
echo -e " ${INFO} Pi-hole blocking is disabled"
adList="${adList}.bck"
blackList="${blackList}.bck"
# Generate new sqlite3 file from schema template
generate_gravity_database() {
sqlite3 "${gravityDBfile}" < "${gravityDBschema}"
}
# Import domains from file and store them in the specified database table
database_table_from_file() {
# Define locals
local table source backup_path backup_file
table="${1}"
source="${2}"
backup_path="${piholeDir}/migration_backup"
backup_file="${backup_path}/$(basename "${2}")"
# Truncate table
output=$( { sqlite3 "${gravityDBfile}" <<< "DELETE FROM ${table};"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
echo -e "\\n ${CROSS} Unable to truncate ${table} database ${gravityDBfile}\\n ${output}"
gravity_Cleanup "error"
fi
local tmpFile
tmpFile="$(mktemp -p "/tmp" --suffix=".gravity")"
local timestamp
timestamp="$(date --utc +'%s')"
local inputfile
if [[ "${table}" == "gravity" ]]; then
# No need to modify the input data for the gravity table
inputfile="${source}"
else
echo -e " ${INFO} Pi-hole blocking is enabled"
# Apply format for white-, blacklist, regex, and adlists tables
local rowid
declare -i rowid
rowid=1
# Read file line by line
grep -v '^ *#' < "${source}" | while IFS= read -r domain
do
# Only add non-empty lines
if [[ ! -z "${domain}" ]]; then
echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\"" >> "${tmpFile}"
rowid+=1
fi
done
inputfile="${tmpFile}"
fi
# Store domains in database table specified by ${table}
# Use printf as .mode and .import need to be on separate lines
# see https://unix.stackexchange.com/a/445615/83260
output=$( { printf ".mode csv\\n.import \"%s\" %s\\n" "${inputfile}" "${table}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
echo -e "\\n ${CROSS} Unable to fill table ${table} in database ${gravityDBfile}\\n ${output}"
gravity_Cleanup "error"
fi
# Delete tmpfile
rm "${tmpFile}" > /dev/null 2>&1 || \
echo -e " ${CROSS} Unable to remove ${tmpFile}"
# Move source file to backup directory, create directory if not existing
mkdir -p "${backup_path}"
mv "${source}" "${backup_file}" 2> /dev/null || \
echo -e " ${CROSS} Unable to backup ${source} to ${backup_path}"
}
# Migrate pre-v5.0 list files to database-based Pi-hole versions
migrate_to_database() {
# Create database file only if not present
if [ -e "${gravityDBfile}" ]; then
return 0
fi
echo -e " ${INFO} Creating new gravity database"
generate_gravity_database
# Migrate list files to new database
if [[ -e "${adListFile}" ]]; then
# Store adlists domains in database
echo -e " ${INFO} Migrating content of ${adListFile} into new database"
database_table_from_file "adlists" "${adListFile}"
fi
if [[ -e "${blacklistFile}" ]]; then
# Store blacklisted domains in database
echo -e " ${INFO} Migrating content of ${blacklistFile} into new database"
database_table_from_file "blacklist" "${blacklistFile}"
fi
if [[ -e "${whitelistFile}" ]]; then
# Store whitelisted domains in database
echo -e " ${INFO} Migrating content of ${whitelistFile} into new database"
database_table_from_file "whitelist" "${whitelistFile}"
fi
if [[ -e "${regexFile}" ]]; then
# Store regex domains in database
echo -e " ${INFO} Migrating content of ${regexFile} into new database"
database_table_from_file "regex" "${regexFile}"
fi
}
@ -157,15 +240,9 @@ gravity_CheckDNSResolutionAvailable() {
gravity_GetBlocklistUrls() {
echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..."
if [[ -f "${adListDefault}" ]] && [[ -f "${adListFile}" ]]; then
# Remove superceded $adListDefault file
rm "${adListDefault}" 2> /dev/null || \
echo -e " ${CROSS} Unable to remove ${adListDefault}"
fi
# Retrieve source URLs from $adListFile
# Logic: Remove comments and empty lines
mapfile -t sources <<< "$(grep -v -E "^(#|$)" "${adListFile}" 2> /dev/null)"
# Retrieve source URLs from gravity database
# We source only enabled adlists, sqlite3 stores boolean values as 0 (false) or 1 (true)
mapfile -t sources <<< "$(sqlite3 "${gravityDBfile}" "SELECT address FROM vw_adlists;" 2> /dev/null)"
# Parse source domains from $sources
mapfile -t sourceDomains <<< "$(
@ -182,11 +259,12 @@ gravity_GetBlocklistUrls() {
if [[ -n "${sources[*]}" ]] && [[ -n "${sourceDomains[*]}" ]]; then
echo -e "${OVER} ${TICK} ${str}"
return 0
else
echo -e "${OVER} ${CROSS} ${str}"
echo -e " ${INFO} No source list found, or it is empty"
echo ""
haveSourceUrls=false
return 1
fi
}
@ -214,11 +292,9 @@ gravity_SetDownloadOptions() {
*) cmd_ext="";;
esac
if [[ "${skipDownload}" == false ]]; then
echo -e " ${INFO} Target: ${domain} (${url##*/})"
gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}"
echo ""
fi
echo -e " ${INFO} Target: ${domain} (${url##*/})"
gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}"
echo ""
done
gravity_Blackbody=true
}
@ -335,14 +411,17 @@ gravity_ParseFileIntoDomains() {
# Most of the lists downloaded are already in hosts file format but the spacing/formating is not contigious
# This helps with that and makes it easier to read
# It also helps with debugging so each stage of the script can be researched more in depth
# Awk -F splits on given IFS, we grab the right hand side (chops trailing #coments and /'s to grab the domain only.
# Last awk command takes non-commented lines and if they have 2 fields, take the right field (the domain) and leave
# the left (IP address), otherwise grab the single field.
< ${source} awk -F '#' '{print $1}' | \
awk -F '/' '{print $1}' | \
awk '($1 !~ /^#/) { if (NF>1) {print $2} else {print $1}}' | \
sed -nr -e 's/\.{2,}/./g' -e '/\./p' > ${destination}
# 1) Remove carriage returns
# 2) Convert all characters to lowercase
# 3) Remove lines containing "#" or "/"
# 4) Remove leading tabs, spaces, etc.
# 5) Delete lines not matching domain names
< "${source}" tr -d '\r' | \
tr '[:upper:]' '[:lower:]' | \
sed -r '/(\/|#).*$/d' | \
sed -r 's/^.*\s+//g' | \
sed -r '/([^\.]+\.)+[^\.]{2,}/!d' > "${destination}"
chmod 644 "${destination}"
return 0
fi
@ -373,6 +452,7 @@ gravity_ParseFileIntoDomains() {
if($0 ~ /^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/) { $0="" }
if($0) { print $0 }
}' "${source}" > "${destination}"
chmod 644 "${destination}"
# Determine if there are Adblock exception rules
# https://adblockplus.org/filters
@ -390,6 +470,7 @@ gravity_ParseFileIntoDomains() {
# Remove exceptions
comm -23 "${destination}" <(sort "${destination}.exceptionsFile.tmp") > "${source}"
mv "${source}" "${destination}"
chmod 644 "${destination}"
fi
echo -e "${OVER} ${TICK} Format: Adblock"
@ -413,11 +494,13 @@ gravity_ParseFileIntoDomains() {
# Print if nonempty
length { print }
' "${source}" 2> /dev/null > "${destination}"
chmod 644 "${destination}"
echo -e "${OVER} ${TICK} Format: URL"
else
# Default: Keep hosts/domains file in same format as it was downloaded
output=$( { mv "${source}" "${destination}"; } 2>&1 )
chmod 644 "${destination}"
if [[ ! -e "${destination}" ]]; then
echo -e "\\n ${CROSS} Unable to move tmp file to ${piholeDir}
@ -432,12 +515,11 @@ gravity_ConsolidateDownloadedBlocklists() {
local str lastLine
str="Consolidating blocklists"
if [[ "${haveSourceUrls}" == true ]]; then
echo -ne " ${INFO} ${str}..."
fi
echo -ne " ${INFO} ${str}..."
# Empty $matterAndLight if it already exists, otherwise, create it
: > "${piholeDir}/${matterAndLight}"
chmod 644 "${piholeDir}/${matterAndLight}"
# Loop through each *.domains file
for i in "${activeDomains[@]}"; do
@ -453,9 +535,8 @@ gravity_ConsolidateDownloadedBlocklists() {
fi
fi
done
if [[ "${haveSourceUrls}" == true ]]; then
echo -e "${OVER} ${TICK} ${str}"
fi
echo -e "${OVER} ${TICK} ${str}"
}
# Parse consolidated list into (filtered, unique) domains-only format
@ -463,67 +544,45 @@ gravity_SortAndFilterConsolidatedList() {
local str num
str="Extracting domains from blocklists"
if [[ "${haveSourceUrls}" == true ]]; then
echo -ne " ${INFO} ${str}..."
fi
echo -ne " ${INFO} ${str}..."
# Parse into hosts file
# Parse into file
gravity_ParseFileIntoDomains "${piholeDir}/${matterAndLight}" "${piholeDir}/${parsedMatter}"
# Format $parsedMatter line total as currency
num=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${parsedMatter}")")
if [[ "${haveSourceUrls}" == true ]]; then
echo -e "${OVER} ${TICK} ${str}"
fi
echo -e " ${INFO} Number of domains being pulled in by gravity: ${COL_BLUE}${num}${COL_NC}"
str="Removing duplicate domains"
if [[ "${haveSourceUrls}" == true ]]; then
echo -ne " ${INFO} ${str}..."
fi
echo -e "${OVER} ${TICK} ${str}"
echo -e " ${INFO} Gravity pulled in ${COL_BLUE}${num}${COL_NC} domains"
str="Removing duplicate domains"
echo -ne " ${INFO} ${str}..."
sort -u "${piholeDir}/${parsedMatter}" > "${piholeDir}/${preEventHorizon}"
chmod 644 "${piholeDir}/${preEventHorizon}"
echo -e "${OVER} ${TICK} ${str}"
if [[ "${haveSourceUrls}" == true ]]; then
echo -e "${OVER} ${TICK} ${str}"
# Format $preEventHorizon line total as currency
num=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${preEventHorizon}")")
echo -e " ${INFO} Number of unique domains trapped in the Event Horizon: ${COL_BLUE}${num}${COL_NC}"
fi
}
# Whitelist user-defined domains
gravity_Whitelist() {
local num str
if [[ ! -f "${whitelistFile}" ]]; then
echo -e " ${INFO} Nothing to whitelist!"
return 0
fi
num=$(wc -l < "${whitelistFile}")
str="Number of whitelisted domains: ${num}"
# Format $preEventHorizon line total as currency
num=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${preEventHorizon}")")
str="Storing ${COL_BLUE}${num}${COL_NC} unique blocking domains in database"
echo -ne " ${INFO} ${str}..."
# Print everything from preEventHorizon into whitelistMatter EXCEPT domains in $whitelistFile
comm -23 "${piholeDir}/${preEventHorizon}" <(sort "${whitelistFile}") > "${piholeDir}/${whitelistMatter}"
echo -e "${OVER} ${INFO} ${str}"
database_table_from_file "gravity" "${piholeDir}/${preEventHorizon}"
echo -e "${OVER} ${TICK} ${str}"
}
# Output count of blacklisted domains and regex filters
gravity_ShowBlockCount() {
# Report number of entries in a table
gravity_Table_Count() {
local table="${1}"
local str="${2}"
local num
num="$(sqlite3 "${gravityDBfile}" "SELECT COUNT(*) FROM ${table} WHERE enabled = 1;")"
echo -e " ${INFO} Number of ${str}: ${num}"
}
if [[ -f "${blacklistFile}" ]]; then
num=$(printf "%'.0f" "$(wc -l < "${blacklistFile}")")
echo -e " ${INFO} Number of blacklisted domains: ${num}"
fi
if [[ -f "${regexFile}" ]]; then
num=$(grep -cv "^#" "${regexFile}")
echo -e " ${INFO} Number of regex filters: ${num}"
fi
# Output count of blacklisted domains and regex filters
gravity_ShowCount() {
gravity_Table_Count "blacklist" "blacklisted domains"
gravity_Table_Count "whitelist" "whitelisted domains"
gravity_Table_Count "regex" "regex filters"
}
# Parse list of domains into hosts format
@ -543,7 +602,7 @@ gravity_ParseDomainsIntoHosts() {
}
# Create "localhost" entries into hosts format
gravity_ParseLocalDomains() {
gravity_generateLocalList() {
local hostname
if [[ -s "/etc/hostname" ]]; then
@ -559,6 +618,7 @@ gravity_ParseLocalDomains() {
# Empty $localList if it already exists, otherwise, create it
: > "${localList}"
chmod 644 "${localList}"
gravity_ParseDomainsIntoHosts "${localList}.tmp" "${localList}"
@ -568,40 +628,6 @@ gravity_ParseLocalDomains() {
fi
}
# Create primary blacklist entries
gravity_ParseBlacklistDomains() {
local output status
# Empty $accretionDisc if it already exists, otherwise, create it
: > "${piholeDir}/${accretionDisc}"
if [[ -f "${piholeDir}/${whitelistMatter}" ]]; then
mv "${piholeDir}/${whitelistMatter}" "${piholeDir}/${accretionDisc}"
else
# There was no whitelist file, so use preEventHorizon instead of whitelistMatter.
cp "${piholeDir}/${preEventHorizon}" "${piholeDir}/${accretionDisc}"
fi
# Move the file over as /etc/pihole/gravity.list so dnsmasq can use it
output=$( { mv "${piholeDir}/${accretionDisc}" "${adList}"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
echo -e "\\n ${CROSS} Unable to move ${accretionDisc} from ${piholeDir}\\n ${output}"
gravity_Cleanup "error"
fi
}
# Create user-added blacklist entries
gravity_ParseUserDomains() {
if [[ ! -f "${blacklistFile}" ]]; then
return 0
fi
# Copy the file over as /etc/pihole/black.list so dnsmasq can use it
cp "${blacklistFile}" "${blackList}" 2> /dev/null || \
echo -e "\\n ${CROSS} Unable to move ${blacklistFile##*/} to ${piholeDir}"
}
# Trap Ctrl-C
gravity_Trap() {
trap '{ echo -e "\\n\\n ${INFO} ${COL_LIGHT_RED}User-abort detected${COL_NC}"; gravity_Cleanup "error"; }' INT
@ -633,6 +659,21 @@ gravity_Cleanup() {
echo -e "${OVER} ${TICK} ${str}"
if ${optimize_database} ; then
str="Optimizing domains database"
echo -ne " ${INFO} ${str}..."
# Run VACUUM command on database to optimize it
output=$( { sqlite3 "${gravityDBfile}" "VACUUM;"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
echo -e "\\n ${CROSS} Unable to optimize gravity database ${gravityDBfile}\\n ${output}"
error="error"
else
echo -e "${OVER} ${TICK} ${str}"
fi
fi
# Only restart DNS service if offline
if ! pidof ${resolver} &> /dev/null; then
"${PIHOLE_COMMAND}" restartdns
@ -659,17 +700,17 @@ Options:
for var in "$@"; do
case "${var}" in
"-f" | "--force" ) forceDelete=true;;
"-o" | "--optimize" ) optimize_database=true;;
"-h" | "--help" ) helpFunc;;
"-sd" | "--skip-download" ) skipDownload=true;;
"-b" | "--blacklist-only" ) listType="blacklist";;
"-w" | "--whitelist-only" ) listType="whitelist";;
"-wild" | "--wildcard-only" ) listType="wildcard"; dnsRestartType="restart";;
esac
done
# Trap Ctrl-C
gravity_Trap
# Move possibly existing legacy files to the gravity database
migrate_to_database
if [[ "${forceDelete:-}" == true ]]; then
str="Deleting existing list cache"
echo -ne "${INFO} ${str}..."
@ -678,56 +719,24 @@ if [[ "${forceDelete:-}" == true ]]; then
echo -e "${OVER} ${TICK} ${str}"
fi
detect_pihole_blocking_status
# Determine which functions to run
if [[ "${skipDownload}" == false ]]; then
# Gravity needs to download blocklists
gravity_CheckDNSResolutionAvailable
gravity_GetBlocklistUrls
if [[ "${haveSourceUrls}" == true ]]; then
gravity_SetDownloadOptions
fi
# Gravity downloads blocklists next
gravity_CheckDNSResolutionAvailable
if gravity_GetBlocklistUrls; then
gravity_SetDownloadOptions
# Build preEventHorizon
gravity_ConsolidateDownloadedBlocklists
gravity_SortAndFilterConsolidatedList
else
# Gravity needs to modify Blacklist/Whitelist/Wildcards
echo -e " ${INFO} Using cached Event Horizon list..."
numberOf=$(printf "%'.0f" "$(wc -l < "${piholeDir}/${preEventHorizon}")")
echo -e " ${INFO} ${COL_BLUE}${numberOf}${COL_NC} unique domains trapped in the Event Horizon"
fi
# Perform when downloading blocklists, or modifying the whitelist
if [[ "${skipDownload}" == false ]] || [[ "${listType}" == "whitelist" ]]; then
gravity_Whitelist
fi
convert_wildcard_to_regex
gravity_ShowBlockCount
# Perform when downloading blocklists, or modifying the white/blacklist (not wildcards)
if [[ "${skipDownload}" == false ]] || [[ "${listType}" == *"list" ]]; then
str="Parsing domains into hosts format"
echo -ne " ${INFO} ${str}..."
gravity_ParseUserDomains
# Perform when downloading blocklists
if [[ ! "${listType:-}" == "blacklist" ]]; then
gravity_ParseLocalDomains
gravity_ParseBlacklistDomains
fi
echo -e "${OVER} ${TICK} ${str}"
gravity_Cleanup
fi
# Create local.list
gravity_generateLocalList
gravity_ShowCount
gravity_Cleanup
echo ""
# Determine if DNS has been restarted by this instance of gravity
if [[ -z "${dnsWasOffline:-}" ]]; then
# Use "force-reload" when restarting dnsmasq for everything but Wildcards
"${PIHOLE_COMMAND}" restartdns "${dnsRestartType:-force-reload}"
"${PIHOLE_COMMAND}" restartdns reload
fi
"${PIHOLE_COMMAND}" status

@ -10,10 +10,8 @@
# Please see LICENSE file for your rights under this license.
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
readonly gravitylist="/etc/pihole/gravity.list"
readonly blacklist="/etc/pihole/black.list"
# setupVars is not readonly here because in some funcitons (checkout),
# setupVars is not readonly here because in some functions (checkout),
# it might get set again when the installer is sourced. This causes an
# error due to modifying a readonly variable.
setupVars="/etc/pihole/setupVars.conf"
@ -148,14 +146,6 @@ Time:
echo -e " ${INFO} Blocking already disabled, nothing to do"
exit 0
fi
if [[ -e "${gravitylist}" ]]; then
mv "${gravitylist}" "${gravitylist}.bck"
echo "" > "${gravitylist}"
fi
if [[ -e "${blacklist}" ]]; then
mv "${blacklist}" "${blacklist}.bck"
echo "" > "${blacklist}"
fi
if [[ $# > 1 ]]; then
local error=false
if [[ "${2}" == *"s" ]]; then
@ -204,12 +194,6 @@ Time:
echo -e " ${INFO} Enabling blocking"
local str="Pi-hole Enabled"
if [[ -e "${gravitylist}.bck" ]]; then
mv "${gravitylist}.bck" "${gravitylist}"
fi
if [[ -e "${blacklist}.bck" ]]; then
mv "${blacklist}.bck" "${blacklist}"
fi
sed -i "/BLOCKING_ENABLED=/d" "${setupVars}"
echo "BLOCKING_ENABLED=true" >> "${setupVars}"
fi
@ -313,7 +297,7 @@ tailFunc() {
# Colour everything else as gray
tail -f /var/log/pihole.log | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq[.*[0-9]]),,g" \
-e "s,(.*(gravity.list|black.list|regex.list| config ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \
-e "s,(.*(gravity |black |regex | config ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \
-e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
-e "s,.*,${COL_GRAY}&${COL_NC},"
exit 0

@ -398,6 +398,7 @@ def test_FTL_detect_aarch64_no_errors(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
@ -418,6 +419,7 @@ def test_FTL_detect_armv6l_no_errors(Pihole):
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
@ -439,6 +441,7 @@ def test_FTL_detect_armv7l_no_errors(Pihole):
mock_command('ldd', {'/bin/ls': ('/lib/ld-linux-armhf.so.3', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
@ -455,6 +458,7 @@ def test_FTL_detect_x86_64_no_errors(Pihole):
'''
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLdetect
''')
expected_stdout = info_box + ' FTL Checks...'
@ -471,6 +475,7 @@ def test_FTL_detect_unknown_no_errors(Pihole):
mock_command('uname', {'-m': ('mips', '0')}, Pihole)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLdetect
''')
expected_stdout = 'Not able to detect architecture (unknown: mips)'
@ -484,6 +489,7 @@ def test_FTL_download_aarch64_no_errors(Pihole):
download_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
binary="pihole-FTL-aarch64-linux-gnu"
create_pihole_user
FTLinstall
''')
expected_stdout = tick_box + ' Downloading and Installing FTL'
@ -498,6 +504,7 @@ def test_FTL_download_unknown_fails_no_errors(Pihole):
download_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
binary="pihole-FTL-mips"
create_pihole_user
FTLinstall
''')
expected_stdout = cross_box + ' Downloading and Installing FTL'
@ -514,6 +521,7 @@ def test_FTL_download_binary_unset_no_errors(Pihole):
'''
download_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLinstall
''')
expected_stdout = cross_box + ' Downloading and Installing FTL'
@ -530,6 +538,7 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
'''
installed_binary = Pihole.run('''
source /opt/pihole/basic-install.sh
create_pihole_user
FTLdetect
pihole-FTL version
''')

Loading…
Cancel
Save