-#
+The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content without installing any client-side software.
-The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content, without installing any client-side software.
-
-- **Easy-to-install**: our versatile installer walks you through the process, and takes less than ten minutes
+- **Easy-to-install**: our versatile installer walks you through the process and takes less than ten minutes
- **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs
- **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://docs.pi-hole.net/main/prerequisites/)
@@ -22,12 +22,10 @@ The Pi-hole® is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) th
- **Versatile**: can optionally function as a [DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026), ensuring *all* your devices are protected automatically
- **Scalable**: [capable of handling hundreds of millions of queries](https://pi-hole.net/2017/05/24/how-much-traffic-can-pi-hole-handle/) when installed on server-grade hardware
- **Modern**: blocks ads over both IPv4 and IPv6
-- **Free**: open source software which helps ensure _you_ are the sole person in control of your privacy
+- **Free**: open source software that helps ensure _you_ are the sole person in control of your privacy
-----
-Master [](https://travis-ci.com/pi-hole/pi-hole) Development [](https://travis-ci.com/pi-hole/pi-hole)
-
## One-Step Automated Install
Those who want to get started quickly and conveniently may install Pi-hole using the following command:
@@ -52,42 +50,46 @@ sudo bash basic-install.sh
wget -O basic-install.sh https://install.pi-hole.net
sudo bash basic-install.sh
```
+### Method 3: Using Docker to deploy Pi-hole
+Please refer to the [Pi-hole docker repo](https://github.com/pi-hole/docker-pi-hole) to use the Official Docker Images.
## [Post-install: Make your network take advantage of Pi-hole](https://docs.pi-hole.net/main/post-install/)
Once the installer has been run, you will need to [configure your router to have **DHCP clients use Pi-hole as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) which ensures that all devices connecting to your network will have content blocked without any further intervention.
-If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); just be sure to disable DHCP on your router first (if it has that feature available).
+If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); be sure to disable DHCP on your router first (if it has that feature available).
-As a last resort, you can always manually set each device to use Pi-hole as their DNS server.
+As a last resort, you can manually set each device to use Pi-hole as their DNS server.
-----
-## Pi-hole is free, but powered by your support
+## Pi-hole is free but powered by your support
-There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained.
+There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software and the importance of keeping it maintained.
Make no mistake: **your support is absolutely vital to help keep us innovating!**
### [Donations](https://pi-hole.net/donate)
-Sending a donation using our Sponsor Button is **extremely helpful** in offsetting a portion of our monthly expenses:
+Donating using our Sponsor Button is **extremely helpful** in offsetting a portion of our monthly expenses:
### Alternative support
If you'd rather not donate (_which is okay!_), there are other ways you can help support us:
-- [Patreon](https://patreon.com/pihole) _Become a patron for rewards_
+- [GitHub Sponsors](https://github.com/sponsors/pi-hole/)
+- [Patreon](https://patreon.com/pihole)
+- [Hetzner Cloud](https://hetzner.cloud/?ref=7aceisRX3AzA) _affiliate link_
- [Digital Ocean](https://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_
- [Stickermule](https://www.stickermule.com/unlock?ref_id=9127301701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_
-- [Amazon](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_
-- Spreading the word about our software, and how you have benefited from it
+- [Amazon US](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_
+- Spreading the word about our software and how you have benefited from it
### Contributing via GitHub
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
-If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions that it asks will help the volunteers quickly understand what you're aiming to achieve.
+If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions it asks will help the volunteers quickly understand what you're aiming to achieve.
You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it and submit a pull request for us to review.
@@ -95,7 +97,9 @@ You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/ma
## Getting in touch with us
-While we are primarily reachable on our [Discourse User Forum](https://discourse.pi-hole.net/), we can also be found on a variety of social media outlets. **Please be sure to check the FAQ's** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
+While we are primarily reachable on our [Discourse User Forum](https://discourse.pi-hole.net/), we can also be found on various social media outlets.
+
+**Please be sure to check the FAQs** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
- [Frequently Asked Questions](https://discourse.pi-hole.net/c/faqs)
- [Feature Requests](https://discourse.pi-hole.net/c/feature-requests?order=votes)
@@ -106,15 +110,30 @@ While we are primarily reachable on our [Discourse User Forum](https://discourse
## Breakdown of Features
+### [Faster-than-light Engine](https://github.com/pi-hole/ftl)
+
+[FTLDNS](https://github.com/pi-hole/ftl) is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
+
+Some of the statistics you can integrate include:
+
+- Total number of domains being blocked
+- Total number of DNS queries today
+- Total number of ads blocked today
+- Percentage of ads blocked
+- Unique domains
+- Queries forwarded (to your chosen upstream DNS server)
+- Queries cached
+- Unique clients
+
+Access the API via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can find out [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
+
### The Command Line Interface
-The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the functionality necessary to be able to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`.
-
-
+The [pihole](https://docs.pi-hole.net/core/pihole-command/) command has all the functionality necessary to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`.
Some notable features include:
-- [Whitelisting, Blacklisting and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex)
+- [Whitelisting, Blacklisting, and Regex](https://docs.pi-hole.net/core/pihole-command/#whitelisting-blacklisting-and-regex)
- [Debugging utility](https://docs.pi-hole.net/core/pihole-command/#debugger)
- [Viewing the live log file](https://docs.pi-hole.net/core/pihole-command/#tail)
- [Updating Ad Lists](https://docs.pi-hole.net/core/pihole-command/#gravity)
@@ -128,11 +147,9 @@ You can read our [Core Feature Breakdown](https://docs.pi-hole.net/core/pihole-c
This [optional dashboard](https://github.com/pi-hole/AdminLTE) allows you to view stats, change settings, and configure your Pi-hole. It's the power of the Command Line Interface, with none of the learning curve!
-
-
Some notable features include:
-- Mobile friendly interface
+- Mobile-friendly interface
- Password protection
- Detailed graphs and doughnut charts
- Top lists of domains and clients
@@ -145,21 +162,3 @@ There are several ways to [access the dashboard](https://discourse.pi-hole.net/t
1. `http://pi.hole/admin/` (when using Pi-hole as your DNS server)
2. `http:///admin/`
-3. `http://pi.hole/` (when using Pi-hole as your DNS server)
-
-## Faster-than-light Engine
-
-FTLDNS is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*!
-
-Some of the statistics you can integrate include:
-
-- Total number of domains being blocked
-- Total number of DNS queries today
-- Total number of ads blocked today
-- Percentage of ads blocked
-- Unique domains
-- Queries forwarded (to your chosen upstream DNS server)
-- Queries cached
-- Unique clients
-
-The API can be accessed via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can find out [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf
index e243e91a..02bc93bf 100644
--- a/advanced/01-pihole.conf
+++ b/advanced/01-pihole.conf
@@ -39,6 +39,4 @@ cache-size=@CACHE_SIZE@
log-queries
log-facility=/var/log/pihole.log
-local-ttl=2
-
log-async
diff --git a/advanced/06-rfc6761.conf b/advanced/06-rfc6761.conf
new file mode 100644
index 00000000..e03569e8
--- /dev/null
+++ b/advanced/06-rfc6761.conf
@@ -0,0 +1,41 @@
+# Pi-hole: A black hole for Internet advertisements
+# (c) 2021 Pi-hole, LLC (https://pi-hole.net)
+# Network-wide ad blocking via your own hardware.
+#
+# RFC 6761 config file for Pi-hole
+#
+# This file is copyright under the latest version of the EUPL.
+# Please see LICENSE file for your rights under this license.
+
+###############################################################################
+# FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
+# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
+# #
+# CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE #
+# WITHIN /etc/dnsmasq.d/yourname.conf #
+###############################################################################
+
+# RFC 6761: Caching DNS servers SHOULD recognize
+# test, localhost, invalid
+# names as special and SHOULD NOT attempt to look up NS records for them, or
+# otherwise query authoritative DNS servers in an attempt to resolve these
+# names.
+server=/test/
+server=/localhost/
+server=/invalid/
+
+# The same RFC requests something similar for
+# 16.172.in-addr.arpa. 22.172.in-addr.arpa. 27.172.in-addr.arpa.
+# 17.172.in-addr.arpa. 30.172.in-addr.arpa. 28.172.in-addr.arpa.
+# 18.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa.
+# 19.172.in-addr.arpa. 24.172.in-addr.arpa. 31.172.in-addr.arpa.
+# 20.172.in-addr.arpa. 25.172.in-addr.arpa. 168.192.in-addr.arpa.
+# Pi-hole implements this via the dnsmasq option "bogus-priv" (see
+# 01-pihole.conf) because this also covers IPv6.
+
+# OpenWRT furthermore blocks bind, local, onion domains
+# see https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob_plain;f=package/network/services/dnsmasq/files/rfc6761.conf;hb=HEAD
+# and https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
+# We do not include the ".local" rule ourselves, see https://github.com/pi-hole/pi-hole/pull/4282#discussion_r689112972
+server=/bind/
+server=/onion/
diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh
index 3126ed25..3f85bdfc 100755
--- a/advanced/Scripts/chronometer.sh
+++ b/advanced/Scripts/chronometer.sh
@@ -498,10 +498,6 @@ chronoFunc() {
printFunc " RAM usage: " "$ram_perc%" "$ram_info"
printFunc " HDD usage: " "$disk_perc" "$disk_info"
- if [[ "$scr_lines" -gt 17 ]] && [[ "$chrono_width" != "small" ]]; then
- printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}" "$lan_info"
- fi
-
if [[ "$DHCP_ACTIVE" == "true" ]]; then
printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info"
fi
@@ -559,7 +555,7 @@ Calculates stats and displays to an LCD
Options:
-j, --json Output stats as JSON formatted string
-r, --refresh Set update frequency (in seconds)
- -e, --exit Output stats and exit witout refreshing
+ -e, --exit Output stats and exit without refreshing
-h, --help Display this help text"
fi
diff --git a/advanced/Scripts/database_migration/gravity-db.sh b/advanced/Scripts/database_migration/gravity-db.sh
old mode 100644
new mode 100755
index 282ea07b..0fecf34a
--- a/advanced/Scripts/database_migration/gravity-db.sh
+++ b/advanced/Scripts/database_migration/gravity-db.sh
@@ -111,9 +111,21 @@ upgrade_gravityDB(){
version=12
fi
if [[ "$version" == "12" ]]; then
- # Add column date_updated to alist table
+ # Add column date_updated to adlist table
echo -e " ${INFO} Upgrading gravity database from version 12 to 13"
sqlite3 "${database}" < "${scriptPath}/12_to_13.sql"
version=13
fi
+ if [[ "$version" == "13" ]]; then
+ # Add columns number and status to adlist table
+ echo -e " ${INFO} Upgrading gravity database from version 13 to 14"
+ sqlite3 "${database}" < "${scriptPath}/13_to_14.sql"
+ version=14
+ fi
+ if [[ "$version" == "14" ]]; then
+ # Changes the vw_adlist created in 5_to_6
+ echo -e " ${INFO} Upgrading gravity database from version 14 to 15"
+ sqlite3 "${database}" < "${scriptPath}/14_to_15.sql"
+ version=15
+fi
}
diff --git a/advanced/Scripts/database_migration/gravity/13_to_14.sql b/advanced/Scripts/database_migration/gravity/13_to_14.sql
new file mode 100644
index 00000000..0a465d1d
--- /dev/null
+++ b/advanced/Scripts/database_migration/gravity/13_to_14.sql
@@ -0,0 +1,13 @@
+.timeout 30000
+
+PRAGMA FOREIGN_KEYS=OFF;
+
+BEGIN TRANSACTION;
+
+ALTER TABLE adlist ADD COLUMN number INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE adlist ADD COLUMN invalid_domains INTEGER NOT NULL DEFAULT 0;
+ALTER TABLE adlist ADD COLUMN status INTEGER NOT NULL DEFAULT 0;
+
+UPDATE info SET value = 14 WHERE property = 'version';
+
+COMMIT;
diff --git a/advanced/Scripts/database_migration/gravity/14_to_15.sql b/advanced/Scripts/database_migration/gravity/14_to_15.sql
new file mode 100644
index 00000000..41cb7517
--- /dev/null
+++ b/advanced/Scripts/database_migration/gravity/14_to_15.sql
@@ -0,0 +1,15 @@
+.timeout 30000
+
+PRAGMA FOREIGN_KEYS=OFF;
+
+BEGIN TRANSACTION;
+DROP VIEW vw_adlist;
+
+CREATE VIEW vw_adlist AS SELECT DISTINCT address, id
+ FROM adlist
+ WHERE enabled = 1
+ ORDER BY id;
+
+UPDATE info SET value = 15 WHERE property = 'version';
+
+COMMIT;
diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh
index 16bb2001..5bd42d55 100755
--- a/advanced/Scripts/list.sh
+++ b/advanced/Scripts/list.sh
@@ -1,4 +1,6 @@
#!/usr/bin/env bash
+# shellcheck disable=SC1090
+
# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
@@ -9,11 +11,19 @@
# Please see LICENSE file for your rights under this license.
# Globals
-basename=pihole
-piholeDir=/etc/"${basename}"
-gravityDBfile="${piholeDir}/gravity.db"
+piholeDir="/etc/pihole"
+GRAVITYDB="${piholeDir}/gravity.db"
+# Source pihole-FTL from install script
+pihole_FTL="${piholeDir}/pihole-FTL.conf"
+if [[ -f "${pihole_FTL}" ]]; then
+ source "${pihole_FTL}"
+fi
-reload=false
+# Set this only after sourcing pihole-FTL.conf as the gravity database path may
+# have changed
+gravityDBfile="${GRAVITYDB}"
+
+noReloadRequested=false
addmode=true
verbose=true
wildcard=false
@@ -25,6 +35,7 @@ typeId=""
comment=""
declare -i domaincount
domaincount=0
+reload=false
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
@@ -112,7 +123,7 @@ ProcessDomainList() {
for dom in "${domList[@]}"; do
# Format domain into regex filter if requested
if [[ "${wildcard}" == true ]]; then
- dom="(^|\\.)${dom//\./\\.}$"
+ dom="(\\.|^)${dom//\./\\.}$"
fi
# Logic: If addmode then add to desired list and remove from the other;
@@ -232,13 +243,13 @@ Displaylist() {
NukeList() {
count=$(sqlite3 "${gravityDBfile}" "SELECT COUNT(1) FROM domainlist WHERE type = ${typeId};")
- listname="$(GetListnameFromTypeId "${typeId}")"
+ listname="$(GetListnameFromTypeId "${typeId}")"
if [ "$count" -gt 0 ];then
sqlite3 "${gravityDBfile}" "DELETE FROM domainlist WHERE type = ${typeId};"
echo " ${TICK} Removed ${count} domain(s) from the ${listname}"
else
echo " ${INFO} ${listname} already empty. Nothing to do!"
- fi
+ fi
exit 0;
}
@@ -258,7 +269,7 @@ while (( "$#" )); do
"--white-wild" | "white-wild" ) typeId=2; wildcard=true;;
"--wild" | "wildcard" ) typeId=3; wildcard=true;;
"--regex" | "regex" ) typeId=3;;
- "-nr"| "--noreload" ) reload=false;;
+ "-nr"| "--noreload" ) noReloadRequested=true;;
"-d" | "--delmode" ) addmode=false;;
"-q" | "--quiet" ) verbose=false;;
"-h" | "--help" ) helpFunc;;
@@ -284,6 +295,6 @@ if $web; then
echo "DONE"
fi
-if [[ "${reload}" != false ]]; then
+if [[ ${reload} == true && ${noReloadRequested} == false ]]; then
pihole restartdns reload-lists
fi
diff --git a/advanced/Scripts/piholeARPTable.sh b/advanced/Scripts/piholeARPTable.sh
index b6b552c9..66d05bf9 100755
--- a/advanced/Scripts/piholeARPTable.sh
+++ b/advanced/Scripts/piholeARPTable.sh
@@ -38,7 +38,7 @@ flushARP(){
# Truncate network_addresses table in pihole-FTL.db
# This needs to be done before we can truncate the network table due to
- # foreign key contraints
+ # foreign key constraints
if ! output=$(sqlite3 "${DBFILE}" "DELETE FROM network_addresses" 2>&1); then
echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table"
echo " Database location: ${DBFILE}"
diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh
old mode 100644
new mode 100755
index 1c1b16a4..4c0a4f40
--- a/advanced/Scripts/piholeCheckout.sh
+++ b/advanced/Scripts/piholeCheckout.sh
@@ -166,12 +166,15 @@ checkout() {
checkout_pull_branch "${webInterfaceDir}" "${2}"
elif [[ "${1}" == "ftl" ]] ; then
local path
+ local oldbranch
path="${2}/${binary}"
+ oldbranch="$(pihole-FTL -b)"
if check_download_exists "$path"; then
echo " ${TICK} Branch ${2} exists"
echo "${2}" > /etc/pihole/ftlbranch
chmod 644 /etc/pihole/ftlbranch
+ echo -e " ${INFO} Switching to branch: \"${2}\" from \"${oldbranch}\""
FTLinstall "${binary}"
restart_service pihole-FTL
enable_service pihole-FTL
diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh
index d8d86563..cd615825 100755
--- a/advanced/Scripts/piholeDebug.sh
+++ b/advanced/Scripts/piholeDebug.sh
@@ -56,11 +56,6 @@ FAQ_BAD_ADDRESS="${COL_CYAN}https://discourse.pi-hole.net/t/why-do-i-see-bad-add
# Other URLs we may use
FORUMS_URL="${COL_CYAN}https://discourse.pi-hole.net${COL_NC}"
-TRICORDER_CONTEST="${COL_CYAN}https://pi-hole.net/2016/11/07/crack-our-medical-tricorder-win-a-raspberry-pi-3/${COL_NC}"
-
-# Port numbers used for uploading the debug log
-TRICORDER_NC_PORT_NUMBER=9999
-TRICORDER_SSL_PORT_NUMBER=9998
# Directories required by Pi-hole
# https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684
@@ -78,15 +73,12 @@ HTML_DIRECTORY="/var/www/html"
WEB_GIT_DIRECTORY="${HTML_DIRECTORY}/admin"
#BLOCK_PAGE_DIRECTORY="${HTML_DIRECTORY}/pihole"
SHM_DIRECTORY="/dev/shm"
+ETC="/etc"
# Files required by Pi-hole
# https://discourse.pi-hole.net/t/what-files-does-pi-hole-use/1684
PIHOLE_CRON_FILE="${CRON_D_DIRECTORY}/pihole"
-PIHOLE_DNS_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/01-pihole.conf"
-PIHOLE_DHCP_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/02-pihole-dhcp.conf"
-PIHOLE_WILDCARD_CONFIG_FILE="${DNSMASQ_D_DIRECTORY}/03-wildcard.conf"
-
WEB_SERVER_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/lighttpd.conf"
WEB_SERVER_CUSTOM_CONFIG_FILE="${WEB_SERVER_CONFIG_DIRECTORY}/external.conf"
@@ -141,6 +133,9 @@ PIHOLE_FTL_LOG="$(get_ftl_conf_value "LOGFILE" "${LOG_DIRECTORY}/pihole-FTL.log"
PIHOLE_WEB_SERVER_ACCESS_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/access.log"
PIHOLE_WEB_SERVER_ERROR_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/error.log"
+RESOLVCONF="${ETC}/resolv.conf"
+DNSMASQ_CONF="${ETC}/dnsmasq.conf"
+
# An array of operating system "pretty names" that we officially support
# We can loop through the array at any time to see if it matches a value
#SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS")
@@ -165,9 +160,6 @@ PIHOLE_PROCESSES=( "lighttpd" "pihole-FTL" )
# Store the required directories in an array so it can be parsed through
REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
-"${PIHOLE_DNS_CONFIG_FILE}"
-"${PIHOLE_DHCP_CONFIG_FILE}"
-"${PIHOLE_WILDCARD_CONFIG_FILE}"
"${WEB_SERVER_CONFIG_FILE}"
"${WEB_SERVER_CUSTOM_CONFIG_FILE}"
"${PIHOLE_INSTALL_LOG_FILE}"
@@ -185,7 +177,9 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
"${PIHOLE_DEBUG_LOG}"
"${PIHOLE_FTL_LOG}"
"${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE}"
-"${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}")
+"${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}"
+"${RESOLVCONF}"
+"${DNSMASQ_CONF}")
DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net.
@@ -235,6 +229,7 @@ copy_to_debug_log() {
}
initialize_debug() {
+ local system_uptime
# Clear the screen so the debug log is readable
clear
show_disclaimer
@@ -242,9 +237,13 @@ initialize_debug() {
log_write "${COL_PURPLE}*** [ INITIALIZING ]${COL_NC}"
# Timestamp the start of the log
log_write "${INFO} $(date "+%Y-%m-%d:%H:%M:%S") debug log has been initialized."
+ # Uptime of the system
+ # credits to https://stackoverflow.com/questions/28353409/bash-format-uptime-to-show-days-hours-minutes
+ system_uptime=$(uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}')
+ log_write "${INFO} System has been running for ${system_uptime}"
}
-# This is a function for visually displaying the curent test that is being run.
+# This is a function for visually displaying the current test that is being run.
# Accepts one variable: the name of what is being diagnosed
# Colors do not show in the dasboard, but the icons do: [i], [✓], and [✗]
echo_current_diagnostic() {
@@ -379,7 +378,7 @@ get_program_version() {
# Create a local variable so this function can be safely reused
local program_version
echo_current_diagnostic "${program_name} version"
- # Evalutate the program we are checking, if it is any of the ones below, show the version
+ # Evaluate the program we are checking, if it is any of the ones below, show the version
case "${program_name}" in
"lighttpd") program_version="$(${program_name} -v 2> /dev/null | head -n1 | cut -d '/' -f2 | cut -d ' ' -f1)"
;;
@@ -410,12 +409,12 @@ os_check() {
# This function gets a list of supported OS versions from a TXT record at versions.pi-hole.net
# and determines whether or not the script is running on one of those systems
local remote_os_domain valid_os valid_version detected_os detected_version cmdResult digReturnCode response
- remote_os_domain="versions.pi-hole.net"
+ remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"versions.pi-hole.net"}
detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"')
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
- cmdResult="$(dig +short -t txt ${remote_os_domain} @ns1.pi-hole.net 2>&1; echo $?)"
+ cmdResult="$(dig +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)"
#Get the return code of the previous command (last line)
digReturnCode="${cmdResult##*$'\n'}"
@@ -586,6 +585,27 @@ processor_check() {
fi
}
+disk_usage() {
+ local file_system
+ local hide
+
+ echo_current_diagnostic "Disk usage"
+ mapfile -t file_system < <(df -h)
+
+ # Some lines of df might contain sensitive information like usernames and passwords.
+ # E.g. curlftpfs filesystems (https://www.looklinux.com/mount-ftp-share-on-linux-using-curlftps/)
+ # We are not interested in those lines so we collect keyword, to remove them from the output
+ # Additinal keywords can be added, separated by "|"
+ hide="curlftpfs"
+
+ # only show those lines not containg a sensitive phrase
+ for line in "${file_system[@]}"; do
+ if [[ ! $line =~ $hide ]]; then
+ log_write " ${line}"
+ fi
+ done
+}
+
parse_setup_vars() {
echo_current_diagnostic "Setup variables"
# If the file exists,
@@ -605,43 +625,11 @@ parse_locale() {
parse_file "${pihole_locale}"
}
-does_ip_match_setup_vars() {
- # Check for IPv4 or 6
- local protocol="${1}"
- # IP address to check for
- local ip_address="${2}"
- # See what IP is in the setupVars.conf file
- local setup_vars_ip
- setup_vars_ip=$(< ${PIHOLE_SETUP_VARS_FILE} grep IPV"${protocol}"_ADDRESS | cut -d '=' -f2)
- # If it's an IPv6 address
- if [[ "${protocol}" == "6" ]]; then
- # Strip off the / (CIDR notation)
- if [[ "${ip_address%/*}" == "${setup_vars_ip%/*}" ]]; then
- # if it matches, show it in green
- log_write " ${COL_GREEN}${ip_address%/*}${COL_NC} matches the IP found in ${PIHOLE_SETUP_VARS_FILE}"
- else
- # otherwise show it in red with an FAQ URL
- log_write " ${COL_RED}${ip_address%/*}${COL_NC} does not match the IP found in ${PIHOLE_SETUP_VARS_FILE} (${FAQ_ULA})"
- fi
-
- else
- # if the protocol isn't 6, it's 4 so no need to strip the CIDR notation
- # since it exists in the setupVars.conf that way
- if [[ "${ip_address}" == "${setup_vars_ip}" ]]; then
- # show in green if it matches
- log_write " ${COL_GREEN}${ip_address}${COL_NC} matches the IP found in ${PIHOLE_SETUP_VARS_FILE}"
- else
- # otherwise show it in red
- log_write " ${COL_RED}${ip_address}${COL_NC} does not match the IP found in ${PIHOLE_SETUP_VARS_FILE} (${FAQ_ULA})"
- fi
- fi
-}
-
detect_ip_addresses() {
# First argument should be a 4 or a 6
local protocol=${1}
# Use ip to show the addresses for the chosen protocol
- # Store the values in an arry so they can be looped through
+ # Store the values in an array so they can be looped through
# Get the lines that are in the file(s) and store them in an array for parsing later
mapfile -t ip_addr_list < <(ip -"${protocol}" addr show dev "${PIHOLE_INTERFACE}" | awk -F ' ' '{ for(i=1;i<=NF;i++) if ($i ~ '/^inet/') print $(i+1) }')
@@ -653,8 +641,7 @@ detect_ip_addresses() {
log_write "${TICK} IPv${protocol} address(es) bound to the ${PIHOLE_INTERFACE} interface:"
# Since there may be more than one IP address, store them in an array
for i in "${!ip_addr_list[@]}"; do
- # For each one in the list, print it out
- does_ip_match_setup_vars "${protocol}" "${ip_addr_list[$i]}"
+ log_write " ${ip_addr_list[$i]}"
done
# Print a blank line just for formatting
log_write ""
@@ -663,13 +650,6 @@ detect_ip_addresses() {
log_write "${CROSS} ${COL_RED}No IPv${protocol} address(es) found on the ${PIHOLE_INTERFACE}${COL_NC} interface.\\n"
return 1
fi
- # If the protocol is v6
- if [[ "${protocol}" == "6" ]]; then
- # let the user know that as long as there is one green address, things should be ok
- log_write " ^ Please note that you may have more than one IP address listed."
- log_write " As long as one of them is green, and it matches what is in ${PIHOLE_SETUP_VARS_FILE}, there is no need for concern.\\n"
- log_write " The link to the FAQ is for an issue that sometimes occurs when the IPv6 address changes, which is why we check for it.\\n"
- fi
}
ping_ipv4_or_ipv6() {
@@ -695,7 +675,7 @@ ping_gateway() {
# Check if we are using IPv4 or IPv6
# Find the default gateway using IPv4 or IPv6
local gateway
- gateway="$(ip -"${protocol}" route | grep default | cut -d ' ' -f 3)"
+ gateway="$(ip -"${protocol}" route | grep default | grep "${PIHOLE_INTERFACE}" | cut -d ' ' -f 3)"
# If the gateway variable has a value (meaning a gateway was found),
if [[ -n "${gateway}" ]]; then
@@ -823,7 +803,7 @@ check_x_headers() {
# Do it for the dashboard as well, as the header is different than above
local dashboard
dashboard=$(curl -Is localhost/admin/ | awk '/X-Pi-hole/' | tr -d '\r')
- # Store what the X-Header shoud be in variables for comparison later
+ # Store what the X-Header should be in variables for comparison later
local block_page_working
block_page_working="X-Pi-hole: A black hole for Internet advertisements."
local dashboard_working
@@ -842,12 +822,12 @@ check_x_headers() {
log_write "${COL_RED}${full_curl_output_block_page}${COL_NC}"
fi
- # Same logic applies to the dashbord as above, if the X-Header matches what a working system shoud have,
+ # Same logic applies to the dashboard as above, if the X-Header matches what a working system should have,
if [[ $dashboard == "$dashboard_working" ]]; then
# then we can show a success
log_write "$TICK Web interface X-Header: ${COL_GREEN}${dashboard}${COL_NC}"
else
- # Othewise, it's a failure since the X-Headers either don't exist or have been modified in some way
+ # Otherwise, it's a failure since the X-Headers either don't exist or have been modified in some way
log_write "$CROSS Web interface X-Header: ${COL_RED}X-Header does not match or could not be retrieved.${COL_NC}"
log_write "${COL_RED}${full_curl_output_dashboard}${COL_NC}"
fi
@@ -859,13 +839,13 @@ dig_at() {
# Store the arguments as variables with names
local protocol="${1}"
- local IP="${2}"
echo_current_diagnostic "Name resolution (IPv${protocol}) using a random blocked domain and a known ad-serving domain"
# Set more local variables
# We need to test name resolution locally, via Pi-hole, and via a public resolver
local local_dig
- local pihole_dig
local remote_dig
+ local interfaces
+ local addresses
# Use a static domain that we know has IPv4 and IPv6 to avoid false positives
# Sometimes the randomly chosen domains don't use IPv6, or something else is wrong with them
local remote_url="doubleclick.com"
@@ -874,15 +854,15 @@ dig_at() {
if [[ ${protocol} == "6" ]]; then
# Set the IPv6 variables and record type
local local_address="::1"
- local pihole_address="${IP}"
local remote_address="2001:4860:4860::8888"
+ local sed_selector="inet6"
local record_type="AAAA"
- # Othwerwise, it should be 4
+ # Otherwise, it should be 4
else
# so use the IPv4 values
local local_address="127.0.0.1"
- local pihole_address="${IP}"
local remote_address="8.8.8.8"
+ local sed_selector="inet"
local record_type="A"
fi
@@ -892,32 +872,55 @@ dig_at() {
local random_url
random_url=$(sqlite3 "${PIHOLE_GRAVITY_DB_FILE}" "SELECT domain FROM vw_gravity ORDER BY RANDOM() LIMIT 1")
- # First, do a dig on localhost to see if Pi-hole can use itself to block a domain
- if local_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @${local_address} +short "${record_type}"); then
- # If it can, show success
- log_write "${TICK} ${random_url} ${COL_GREEN}is ${local_dig}${COL_NC} via ${COL_CYAN}localhost$COL_NC (${local_address})"
- else
- # Otherwise, show a failure
- log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} via ${COL_RED}localhost${COL_NC} (${local_address})"
- fi
-
# Next we need to check if Pi-hole can resolve a domain when the query is sent to it's IP address
# This better emulates how clients will interact with Pi-hole as opposed to above where Pi-hole is
# just asing itself locally
- # The default timeouts and tries are reduced in case the DNS server isn't working, so the user isn't waiting for too long
+ # The default timeouts and tries are reduced in case the DNS server isn't working, so the user isn't
+ # waiting for too long
+ #
+ # Turn off history expansion such that the "!" in the sed command cannot do silly things
+ set +H
+ # Get interfaces
+ # sed logic breakdown:
+ # / master /d;
+ # Removes all interfaces that are slaves of others (e.g. virtual docker interfaces)
+ # /UP/!d;
+ # Removes all interfaces which are not UP
+ # s/^[0-9]*: //g;
+ # Removes interface index
+ # s/: <.*//g;
+ # Removes everything after the interface name
+ interfaces="$(ip link show | sed "/ master /d;/UP/!d;s/^[0-9]*: //g;s/: <.*//g;")"
- # If Pi-hole can dig itself from it's IP (not the loopback address)
- if pihole_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${pihole_address}" +short "${record_type}"); then
- # show a success
- log_write "${TICK} ${random_url} ${COL_GREEN}is ${pihole_dig}${COL_NC} via ${COL_CYAN}Pi-hole${COL_NC} (${pihole_address})"
- else
- # Othewise, show a failure
- log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} via ${COL_RED}Pi-hole${COL_NC} (${pihole_address})"
- fi
+ while IFS= read -r iface ; do
+ # Get addresses of current interface
+ # sed logic breakdown:
+ # /inet(|6) /!d;
+ # Removes all lines from ip a that do not contain either "inet " or "inet6 "
+ # s/^.*inet(|6) //g;
+ # Removes all leading whitespace as well as the "inet " or "inet6 " string
+ # s/\/.*$//g;
+ # Removes CIDR and everything thereafter (e.g., scope properties)
+ addresses="$(ip address show dev "${iface}" | sed "/${sed_selector} /!d;s/^.*${sed_selector} //g;s/\/.*$//g;")"
+ if [ -n "${addresses}" ]; then
+ while IFS= read -r local_address ; do
+ # Check if Pi-hole can use itself to block a domain
+ if local_dig=$(dig +tries=1 +time=2 -"${protocol}" "${random_url}" @"${local_address}" +short "${record_type}"); then
+ # If it can, show success
+ log_write "${TICK} ${random_url} ${COL_GREEN}is ${local_dig}${COL_NC} on ${COL_CYAN}${iface}${COL_NC} (${COL_CYAN}${local_address}${COL_NC})"
+ else
+ # Otherwise, show a failure
+ log_write "${CROSS} ${COL_RED}Failed to resolve${COL_NC} ${random_url} on ${COL_RED}${iface}${COL_NC} (${COL_RED}${local_address}${COL_NC})"
+ fi
+ done <<< "${addresses}"
+ else
+ log_write "${TICK} No IPv${protocol} address available on ${COL_CYAN}${iface}${COL_NC}"
+ fi
+ done <<< "${interfaces}"
# Finally, we need to make sure legitimate queries can out to the Internet using an external, public DNS server
# We are using the static remote_url here instead of a random one because we know it works with IPv4 and IPv6
- if remote_dig=$(dig +tries=1 +time=2 -"${protocol}" "${remote_url}" @${remote_address} +short "${record_type}" | head -n1); then
+ if remote_dig=$(dig +tries=1 +time=2 -"${protocol}" "${remote_url}" @"${remote_address}" +short "${record_type}" | head -n1); then
# If successful, the real IP of the domain will be returned instead of Pi-hole's IP
log_write "${TICK} ${remote_url} ${COL_GREEN}is ${remote_dig}${COL_NC} via ${COL_CYAN}a remote, public DNS server${COL_NC} (${remote_address})"
else
@@ -1032,7 +1035,7 @@ parse_file() {
local file_lines
# For each line in the file,
for file_lines in "${file_info[@]}"; do
- if [[ ! -z "${file_lines}" ]]; then
+ if [[ -n "${file_lines}" ]]; then
# don't include the Web password hash
[[ "${file_lines}" =~ ^\#.*$ || ! "${file_lines}" || "${file_lines}" == "WEBPASSWORD="* ]] && continue
# otherwise, display the lines of the file
@@ -1044,14 +1047,10 @@ parse_file() {
}
check_name_resolution() {
- # Check name resoltion from localhost, Pi-hole's IP, and Google's name severs
+ # Check name resolution from localhost, Pi-hole's IP, and Google's name severs
# using the function we created earlier
- dig_at 4 "${IPV4_ADDRESS%/*}"
- # If IPv6 enabled,
- if [[ "${IPV6_ADDRESS}" ]]; then
- # check resolution
- dig_at 6 "${IPV6_ADDRESS%/*}"
- fi
+ dig_at 4
+ dig_at 6
}
# This function can check a directory exists
@@ -1094,13 +1093,17 @@ list_files_in_dir() {
:
elif [[ "${dir_to_parse}" == "${SHM_DIRECTORY}" ]]; then
# SHM file - we do not want to see the content, but we want to see the files and their sizes
- log_write "$(ls -ld "${dir_to_parse}"/"${each_file}")"
+ log_write "$(ls -lhd "${dir_to_parse}"/"${each_file}")"
+ elif [[ "${dir_to_parse}" == "${DNSMASQ_D_DIRECTORY}" ]]; then
+ # in case of the dnsmasq directory inlcuede all files in the debug output
+ log_write "\\n${COL_GREEN}$(ls -lhd "${dir_to_parse}"/"${each_file}")${COL_NC}"
+ make_array_from_file "${dir_to_parse}/${each_file}"
else
# Then, parse the file's content into an array so each line can be analyzed if need be
for i in "${!REQUIRED_FILES[@]}"; do
if [[ "${dir_to_parse}/${each_file}" == "${REQUIRED_FILES[$i]}" ]]; then
# display the filename
- log_write "\\n${COL_GREEN}$(ls -ld "${dir_to_parse}"/"${each_file}")${COL_NC}"
+ log_write "\\n${COL_GREEN}$(ls -lhd "${dir_to_parse}"/"${each_file}")${COL_NC}"
# Check if the file we want to view has a limit (because sometimes we just need a little bit of info from the file, not the entire thing)
case "${dir_to_parse}/${each_file}" in
# If it's Web server error log, give the first and last 25 lines
@@ -1139,6 +1142,7 @@ show_content_of_pihole_files() {
show_content_of_files_in_dir "${WEB_SERVER_LOG_DIRECTORY}"
show_content_of_files_in_dir "${LOG_DIRECTORY}"
show_content_of_files_in_dir "${SHM_DIRECTORY}"
+ show_content_of_files_in_dir "${ETC}"
}
head_tail_log() {
@@ -1239,11 +1243,11 @@ show_groups() {
}
show_adlists() {
- show_db_entries "Adlists" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids,address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;" "4 7 12 100 19 19 50"
+ show_db_entries "Adlists" "SELECT id,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(adlist_by_group.group_id) group_ids,address,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM adlist LEFT JOIN adlist_by_group ON adlist.id = adlist_by_group.adlist_id GROUP BY id;" "5 7 12 100 19 19 50"
}
show_domainlist() {
- show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "4 4 7 12 100 19 19 50"
+ show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "5 4 7 12 100 19 19 50"
}
show_clients() {
@@ -1255,10 +1259,10 @@ show_messages() {
}
analyze_gravity_list() {
- echo_current_diagnostic "Gravity List and Database"
+ echo_current_diagnostic "Gravity Database"
local gravity_permissions
- gravity_permissions=$(ls -ld "${PIHOLE_GRAVITY_DB_FILE}")
+ gravity_permissions=$(ls -lhd "${PIHOLE_GRAVITY_DB_FILE}")
log_write "${COL_GREEN}${gravity_permissions}${COL_NC}"
show_db_entries "Info table" "SELECT property,value FROM info" "20 40"
@@ -1281,77 +1285,88 @@ analyze_gravity_list() {
IFS="$OLD_IFS"
}
-analyze_pihole_log() {
- echo_current_diagnostic "Pi-hole log"
- local head_line
- # Put the current Internal Field Separator into another variable so it can be restored later
- OLD_IFS="$IFS"
- # Get the lines that are in the file(s) and store them in an array for parsing later
- IFS=$'\r\n'
- local pihole_log_permissions
- pihole_log_permissions=$(ls -ld "${PIHOLE_LOG}")
- log_write "${COL_GREEN}${pihole_log_permissions}${COL_NC}"
- local pihole_log_head=()
- mapfile -t pihole_log_head < <(head -n 20 ${PIHOLE_LOG})
- log_write " ${COL_CYAN}-----head of $(basename ${PIHOLE_LOG})------${COL_NC}"
- local error_to_check_for
- local line_to_obfuscate
- local obfuscated_line
- for head_line in "${pihole_log_head[@]}"; do
- # A common error in the pihole.log is when there is a non-hosts formatted file
- # that the DNS server is attempting to read. Since it's not formatted
- # correctly, there will be an entry for "bad address at line n"
- # So we can check for that here and highlight it in red so the user can see it easily
- error_to_check_for=$(echo "${head_line}" | grep 'bad address at')
- # Some users may not want to have the domains they visit sent to us
- # To that end, we check for lines in the log that would contain a domain name
- line_to_obfuscate=$(echo "${head_line}" | grep ': query\|: forwarded\|: reply')
- # If the variable contains a value, it found an error in the log
- if [[ -n ${error_to_check_for} ]]; then
- # So we can print it in red to make it visible to the user
- log_write " ${CROSS} ${COL_RED}${head_line}${COL_NC} (${FAQ_BAD_ADDRESS})"
- else
- # If the variable does not a value (the current default behavior), so do not obfuscate anything
- if [[ -z ${OBFUSCATE} ]]; then
- log_write " ${head_line}"
- # Othwerise, a flag was passed to this command to obfuscate domains in the log
- else
- # So first check if there are domains in the log that should be obfuscated
- if [[ -n ${line_to_obfuscate} ]]; then
- # If there are, we need to use awk to replace only the domain name (the 6th field in the log)
- # so we substitute the domain for the placeholder value
- obfuscated_line=$(echo "${line_to_obfuscate}" | awk -v placeholder="${OBFUSCATED_PLACEHOLDER}" '{sub($6,placeholder); print $0}')
- log_write " ${obfuscated_line}"
- else
- log_write " ${head_line}"
- fi
- fi
- fi
- done
- log_write ""
- # Set the IFS back to what it was
- IFS="$OLD_IFS"
+obfuscated_pihole_log() {
+ local pihole_log=("$@")
+ local line
+ local error_to_check_for
+ local line_to_obfuscate
+ local obfuscated_line
+ for line in "${pihole_log[@]}"; do
+ # A common error in the pihole.log is when there is a non-hosts formatted file
+ # that the DNS server is attempting to read. Since it's not formatted
+ # correctly, there will be an entry for "bad address at line n"
+ # So we can check for that here and highlight it in red so the user can see it easily
+ error_to_check_for=$(echo "${line}" | grep 'bad address at')
+ # Some users may not want to have the domains they visit sent to us
+ # To that end, we check for lines in the log that would contain a domain name
+ line_to_obfuscate=$(echo "${line}" | grep ': query\|: forwarded\|: reply')
+ # If the variable contains a value, it found an error in the log
+ if [[ -n ${error_to_check_for} ]]; then
+ # So we can print it in red to make it visible to the user
+ log_write " ${CROSS} ${COL_RED}${line}${COL_NC} (${FAQ_BAD_ADDRESS})"
+ else
+ # If the variable does not a value (the current default behavior), so do not obfuscate anything
+ if [[ -z ${OBFUSCATE} ]]; then
+ log_write " ${line}"
+ # Othwerise, a flag was passed to this command to obfuscate domains in the log
+ else
+ # So first check if there are domains in the log that should be obfuscated
+ if [[ -n ${line_to_obfuscate} ]]; then
+ # If there are, we need to use awk to replace only the domain name (the 6th field in the log)
+ # so we substitute the domain for the placeholder value
+ obfuscated_line=$(echo "${line_to_obfuscate}" | awk -v placeholder="${OBFUSCATED_PLACEHOLDER}" '{sub($6,placeholder); print $0}')
+ log_write " ${obfuscated_line}"
+ else
+ log_write " ${line}"
+ fi
+ fi
+ fi
+ done
}
-tricorder_use_nc_or_curl() {
- # Users can submit their debug logs using nc (unencrypted) or curl (encrypted) if available
- # Check for curl first since encryption is a good thing
- if command -v curl &> /dev/null; then
- # If the command exists,
- log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission."
- # transmit he log via TLS and store the token returned in a variable
- tricorder_token=$(curl --silent --upload-file ${PIHOLE_DEBUG_LOG} https://tricorder.pi-hole.net:${TRICORDER_SSL_PORT_NUMBER})
- if [ -z "${tricorder_token}" ]; then
- # curl failed, fallback to nc
- log_write " * ${COL_GREEN}curl${COL_NC} failed, falling back to ${COL_YELLOW}netcat${COL_NC} for transmission."
- tricorder_token=$(< ${PIHOLE_DEBUG_LOG} nc tricorder.pi-hole.net ${TRICORDER_NC_PORT_NUMBER})
+analyze_pihole_log() {
+ echo_current_diagnostic "Pi-hole log"
+ local pihole_log_head=()
+ local pihole_log_tail=()
+ local pihole_log_permissions
+ local logging_enabled
+
+ logging_enabled=$(grep -c "^log-queries" /etc/dnsmasq.d/01-pihole.conf)
+ if [[ "${logging_enabled}" == "0" ]]; then
+ # Inform user that logging has been disabled and pihole.log does not contain queries
+ log_write "${INFO} Query logging is disabled"
+ log_write ""
+ fi
+ # Put the current Internal Field Separator into another variable so it can be restored later
+ OLD_IFS="$IFS"
+ # Get the lines that are in the file(s) and store them in an array for parsing later
+ IFS=$'\r\n'
+ pihole_log_permissions=$(ls -lhd "${PIHOLE_LOG}")
+ log_write "${COL_GREEN}${pihole_log_permissions}${COL_NC}"
+ mapfile -t pihole_log_head < <(head -n 20 ${PIHOLE_LOG})
+ log_write " ${COL_CYAN}-----head of $(basename ${PIHOLE_LOG})------${COL_NC}"
+ obfuscated_pihole_log "${pihole_log_head[@]}"
+ log_write ""
+ mapfile -t pihole_log_tail < <(tail -n 20 ${PIHOLE_LOG})
+ log_write " ${COL_CYAN}-----tail of $(basename ${PIHOLE_LOG})------${COL_NC}"
+ obfuscated_pihole_log "${pihole_log_tail[@]}"
+ log_write ""
+ # Set the IFS back to what it was
+ IFS="$OLD_IFS"
+}
+
+curl_to_tricorder() {
+ # Users can submit their debug logs using curl (encrypted)
+ log_write " * Using ${COL_GREEN}curl${COL_NC} for transmission."
+ # transmit the log via TLS and store the token returned in a variable
+ tricorder_token=$(curl --silent --fail --show-error --upload-file ${PIHOLE_DEBUG_LOG} https://tricorder.pi-hole.net 2>&1)
+ if [[ "${tricorder_token}" != "https://tricorder.pi-hole.net/"* ]]; then
+ log_write " * ${COL_GREEN}curl${COL_NC} failed, contact Pi-hole support for assistance."
+ # Log curl error (if available)
+ if [ -n "${tricorder_token}" ]; then
+ log_write " * Error message: ${COL_RED}${tricorder_token}${COL_NC}\\n"
+ tricorder_token=""
fi
- # Otherwise,
- else
- # use net cat
- log_write "${INFO} Using ${COL_YELLOW}netcat${COL_NC} for transmission."
- # Save the token returned by our server in a variable
- tricorder_token=$(< ${PIHOLE_DEBUG_LOG} nc tricorder.pi-hole.net ${TRICORDER_NC_PORT_NUMBER})
fi
}
@@ -1370,14 +1385,13 @@ upload_to_tricorder() {
# Provide information on what they should do with their token
log_write " * The debug log can be uploaded to tricorder.pi-hole.net for sharing with developers only."
- log_write " * For more information, see: ${TRICORDER_CONTEST}"
- log_write " * If available, we'll use openssl to upload the log, otherwise it will fall back to netcat."
+
# If pihole -d is running automatically (usually through the dashboard)
if [[ "${AUTOMATED}" ]]; then
# let the user know
log_write "${INFO} Debug script running in automated mode"
# and then decide again which tool to use to submit it
- tricorder_use_nc_or_curl
+ curl_to_tricorder
# If we're not running in automated mode,
else
echo ""
@@ -1386,7 +1400,7 @@ upload_to_tricorder() {
read -r -p "[?] Would you like to upload the log? [y/N] " response
case ${response} in
# If they say yes, run our function for uploading the log
- [yY][eE][sS]|[yY]) tricorder_use_nc_or_curl;;
+ [yY][eE][sS]|[yY]) curl_to_tricorder;;
# If they choose no, just exit out of the script
*) log_write " * Log will ${COL_GREEN}NOT${COL_NC} be uploaded to tricorder.\\n * A local copy of the debug log can be found at: ${COL_CYAN}${PIHOLE_DEBUG_LOG}${COL_NC}\\n";exit;
esac
@@ -1397,15 +1411,15 @@ upload_to_tricorder() {
# Again, try to make this visually striking so the user realizes they need to do something with this information
# Namely, provide the Pi-hole devs with the token
log_write ""
- log_write "${COL_PURPLE}***********************************${COL_NC}"
- log_write "${COL_PURPLE}***********************************${COL_NC}"
+ log_write "${COL_PURPLE}*****************************************************************${COL_NC}"
+ log_write "${COL_PURPLE}*****************************************************************${COL_NC}\\n"
log_write "${TICK} Your debug token is: ${COL_GREEN}${tricorder_token}${COL_NC}"
- log_write "${COL_PURPLE}***********************************${COL_NC}"
- log_write "${COL_PURPLE}***********************************${COL_NC}"
+ log_write "${INFO}${COL_RED} Logs are deleted 48 hours after upload.${COL_NC}\\n"
+ log_write "${COL_PURPLE}*****************************************************************${COL_NC}"
+ log_write "${COL_PURPLE}*****************************************************************${COL_NC}"
log_write ""
- log_write " * Provide the token above to the Pi-hole team for assistance at"
- log_write " * ${FORUMS_URL}"
- log_write " * Your log will self-destruct on our server after ${COL_RED}48 hours${COL_NC}."
+ log_write " * Provide the token above to the Pi-hole team for assistance at ${FORUMS_URL}"
+
# If no token was generated
else
# Show an error and some help instructions
@@ -1428,6 +1442,7 @@ diagnose_operating_system
check_selinux
check_firewalld
processor_check
+disk_usage
check_networking
check_name_resolution
check_dhcp_servers
diff --git a/advanced/Scripts/piholeLogFlush.sh b/advanced/Scripts/piholeLogFlush.sh
index 51e94d7c..5c6a2c68 100755
--- a/advanced/Scripts/piholeLogFlush.sh
+++ b/advanced/Scripts/piholeLogFlush.sh
@@ -11,6 +11,11 @@
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
+# In case we're running at the same time as a system logrotate, use a
+# separate logrotate state file to prevent stepping on each other's
+# toes.
+STATEFILE="/var/lib/logrotate/pihole"
+
# Determine database location
# Obtain DBFILE=... setting from pihole-FTL.db
# Constructed to return nothing when
@@ -32,7 +37,7 @@ if [[ "$@" == *"once"* ]]; then
# Nightly logrotation
if command -v /usr/sbin/logrotate >/dev/null; then
# Logrotate once
- /usr/sbin/logrotate --force /etc/pihole/logrotate
+ /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
else
# Copy pihole.log over to pihole.log.1
# and empty out pihole.log
@@ -47,8 +52,8 @@ else
# Manual flushing
if command -v /usr/sbin/logrotate >/dev/null; then
# Logrotate twice to move all data out of sight of FTL
- /usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3
- /usr/sbin/logrotate --force /etc/pihole/logrotate
+ /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate; sleep 3
+ /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
else
# Flush both pihole.log and pihole.log.1 (if existing)
echo " " > /var/log/pihole.log
diff --git a/advanced/Scripts/query.sh b/advanced/Scripts/query.sh
index fe9b8ebf..26b4508e 100755
--- a/advanced/Scripts/query.sh
+++ b/advanced/Scripts/query.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# shellcheck disable=SC1090
+
# Pi-hole: A black hole for Internet advertisements
# (c) 2018 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
@@ -11,12 +12,21 @@
# Globals
piholeDir="/etc/pihole"
-gravityDBfile="${piholeDir}/gravity.db"
+GRAVITYDB="${piholeDir}/gravity.db"
options="$*"
all=""
exact=""
blockpage=""
matchType="match"
+# Source pihole-FTL from install script
+pihole_FTL="${piholeDir}/pihole-FTL.conf"
+if [[ -f "${pihole_FTL}" ]]; then
+ source "${pihole_FTL}"
+fi
+
+# Set this only after sourcing pihole-FTL.conf as the gravity database path may
+# have changed
+gravityDBfile="${GRAVITYDB}"
colfile="/opt/pihole/COL_TABLE"
source "${colfile}"
diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh
index f833fc2f..dae04861 100755
--- a/advanced/Scripts/update.sh
+++ b/advanced/Scripts/update.sh
@@ -95,6 +95,10 @@ main() {
# shellcheck disable=1090,2154
source "${setupVars}"
+ # Install packages used by this installation script (necessary if users have removed e.g. git from their systems)
+ package_manager_detect
+ install_dependent_packages "${INSTALLER_DEPS[@]}"
+
# This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!"
diff --git a/advanced/Scripts/version.sh b/advanced/Scripts/version.sh
index f77ee635..86ac45bc 100755
--- a/advanced/Scripts/version.sh
+++ b/advanced/Scripts/version.sh
@@ -153,7 +153,7 @@ versionOutput() {
if [[ -n "$current" ]] && [[ -n "$latest" ]]; then
output="${1^} version is $branch$current (Latest: $latest)"
elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then
- output="Current ${1^} version is $branch$current."
+ output="Current ${1^} version is $branch$current"
elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then
output="Latest ${1^} version is $latest"
elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then
diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh
index da2afb0f..a739d898 100755
--- a/advanced/Scripts/webpage.sh
+++ b/advanced/Scripts/webpage.sh
@@ -44,7 +44,8 @@ Options:
-e, email Set an administrative contact address for the Block Page
-h, --help Show this help dialog
-i, interface Specify dnsmasq's interface listening behavior
- -l, privacylevel Set privacy level (0 = lowest, 3 = highest)"
+ -l, privacylevel Set privacy level (0 = lowest, 3 = highest)
+ -t, teleporter Backup configuration as an archive"
exit 0
}
@@ -53,7 +54,7 @@ add_setting() {
}
delete_setting() {
- sed -i "/${1}/d" "${setupVars}"
+ sed -i "/^${1}/d" "${setupVars}"
}
change_setting() {
@@ -66,7 +67,7 @@ addFTLsetting() {
}
deleteFTLsetting() {
- sed -i "/${1}/d" "${FTLconf}"
+ sed -i "/^${1}/d" "${FTLconf}"
}
changeFTLsetting() {
@@ -83,7 +84,7 @@ add_dnsmasq_setting() {
}
delete_dnsmasq_setting() {
- sed -i "/${1}/d" "${dnsmasqconfig}"
+ sed -i "/^${1}/d" "${dnsmasqconfig}"
}
SetTemperatureUnit() {
@@ -238,18 +239,18 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
# 168.192.in-addr.arpa to 192.168.0.0/16
# 192.in-addr.arpa to 192.0.0.0/8
if [[ "${CONDITIONAL_FORWARDING_REVERSE}" == *"in-addr.arpa" ]];then
- arrRev=("${CONDITIONAL_FORWARDING_REVERSE//./ }")
- case ${#arrRev[@]} in
+ arrRev=("${CONDITIONAL_FORWARDING_REVERSE//./ }")
+ case ${#arrRev[@]} in
6 ) REV_SERVER_CIDR="${arrRev[3]}.${arrRev[2]}.${arrRev[1]}.${arrRev[0]}/32";;
5 ) REV_SERVER_CIDR="${arrRev[2]}.${arrRev[1]}.${arrRev[0]}.0/24";;
4 ) REV_SERVER_CIDR="${arrRev[1]}.${arrRev[0]}.0.0/16";;
- 3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";;
+ 3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";;
esac
else
# Set REV_SERVER_CIDR to whatever value it was set to
REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}"
fi
-
+
# If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it
if [ -z "${REV_SERVER_CIDR}" ]; then
# Convert existing input to /24 subnet (preserves legacy behavior)
@@ -266,17 +267,22 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
delete_setting "CONDITIONAL_FORWARDING_IP"
fi
+ delete_dnsmasq_setting "rev-server"
+
if [[ "${REV_SERVER}" == true ]]; then
add_dnsmasq_setting "rev-server=${REV_SERVER_CIDR},${REV_SERVER_TARGET}"
if [ -n "${REV_SERVER_DOMAIN}" ]; then
+ # Forward local domain names to the CF target, too
add_dnsmasq_setting "server=/${REV_SERVER_DOMAIN}/${REV_SERVER_TARGET}"
fi
- fi
- # Prevent Firefox from automatically switching over to DNS-over-HTTPS
- # This follows https://support.mozilla.org/en-US/kb/configuring-networks-disable-dns-over-https
- # (sourced 7th September 2019)
- add_dnsmasq_setting "server=/use-application-dns.net/"
+ if [[ "${DNS_FQDN_REQUIRED}" != true ]]; then
+ # Forward unqualified names to the CF target only when the "never
+ # forward non-FQDN" option is unticked
+ add_dnsmasq_setting "server=//${REV_SERVER_TARGET}"
+ fi
+
+ fi
# We need to process DHCP settings here as well to account for possible
# changes in the non-FQDN forwarding. This cannot be done in 01-pihole.conf
@@ -426,7 +432,7 @@ dhcp-leasefile=/etc/pihole/dhcp.leases
echo "#quiet-dhcp6
#enable-ra
dhcp-option=option6:dns-server,[::]
-dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime}
+dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,64,3600
ra-param=*,0,0
" >> "${dhcpconfig}"
fi
@@ -564,7 +570,13 @@ AddDHCPStaticAddress() {
RemoveDHCPStaticAddress() {
mac="${args[2]}"
- sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
+ if [[ "$mac" =~ ^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$ ]]; then
+ sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
+ else
+ echo " ${CROSS} Invalid Mac Passed!"
+ exit 1
+ fi
+
}
SetAdminEmail() {
@@ -636,8 +648,11 @@ Interfaces:
Teleporter() {
local datetimestamp
+ local host
datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
- php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.tar.gz"
+ host=$(hostname)
+ host="${host//./_}"
+ php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-${host:-noname}-teleporter_${datetimestamp}.tar.gz"
}
checkDomain()
@@ -694,10 +709,25 @@ AddCustomDNSAddress() {
ip="${args[2]}"
host="${args[3]}"
- echo "${ip} ${host}" >> "${dnscustomfile}"
+ reload="${args[4]}"
- # Restart dnsmasq to load new custom DNS entries
- RestartDNS
+ validHost="$(checkDomain "${host}")"
+ if [[ -n "${validHost}" ]]; then
+ if valid_ip "${ip}" || valid_ip6 "${ip}" ; then
+ echo "${ip} ${validHost}" >> "${dnscustomfile}"
+ else
+ echo -e " ${CROSS} Invalid IP has been passed"
+ exit 1
+ fi
+ else
+ echo " ${CROSS} Invalid Domain passed!"
+ exit 1
+ fi
+
+ # Restart dnsmasq to load new custom DNS entries only if $reload not false
+ if [[ ! $reload == "false" ]]; then
+ RestartDNS
+ fi
}
RemoveCustomDNSAddress() {
@@ -705,10 +735,25 @@ RemoveCustomDNSAddress() {
ip="${args[2]}"
host="${args[3]}"
- sed -i "/${ip} ${host}/d" "${dnscustomfile}"
+ reload="${args[4]}"
- # Restart dnsmasq to update removed custom DNS entries
- RestartDNS
+ validHost="$(checkDomain "${host}")"
+ if [[ -n "${validHost}" ]]; then
+ if valid_ip "${ip}" || valid_ip6 "${ip}" ; then
+ sed -i "/^${ip} ${validHost}$/d" "${dnscustomfile}"
+ else
+ echo -e " ${CROSS} Invalid IP has been passed"
+ exit 1
+ fi
+ else
+ echo " ${CROSS} Invalid Domain passed!"
+ exit 1
+ fi
+
+ # Restart dnsmasq to load new custom DNS entries only if reload is not false
+ if [[ ! $reload == "false" ]]; then
+ RestartDNS
+ fi
}
AddCustomCNAMERecord() {
@@ -716,10 +761,25 @@ AddCustomCNAMERecord() {
domain="${args[2]}"
target="${args[3]}"
- echo "cname=${domain},${target}" >> "${dnscustomcnamefile}"
+ reload="${args[4]}"
- # Restart dnsmasq to load new custom CNAME records
- RestartDNS
+ validDomain="$(checkDomain "${domain}")"
+ if [[ -n "${validDomain}" ]]; then
+ validTarget="$(checkDomain "${target}")"
+ if [[ -n "${validTarget}" ]]; then
+ echo "cname=${validDomain},${validTarget}" >> "${dnscustomcnamefile}"
+ else
+ echo " ${CROSS} Invalid Target Passed!"
+ exit 1
+ fi
+ else
+ echo " ${CROSS} Invalid Domain passed!"
+ exit 1
+ fi
+ # Restart dnsmasq to load new custom CNAME records only if reload is not false
+ if [[ ! $reload == "false" ]]; then
+ RestartDNS
+ fi
}
RemoveCustomCNAMERecord() {
@@ -727,10 +787,26 @@ RemoveCustomCNAMERecord() {
domain="${args[2]}"
target="${args[3]}"
- sed -i "/cname=${domain},${target}/d" "${dnscustomcnamefile}"
+ reload="${args[4]}"
- # Restart dnsmasq to update removed custom CNAME records
- RestartDNS
+ validDomain="$(checkDomain "${domain}")"
+ if [[ -n "${validDomain}" ]]; then
+ validTarget="$(checkDomain "${target}")"
+ if [[ -n "${validTarget}" ]]; then
+ sed -i "/cname=${validDomain},${validTarget}$/d" "${dnscustomcnamefile}"
+ else
+ echo " ${CROSS} Invalid Target Passed!"
+ exit 1
+ fi
+ else
+ echo " ${CROSS} Invalid Domain passed!"
+ exit 1
+ fi
+
+ # Restart dnsmasq to update removed custom CNAME records only if $reload not false
+ if [[ ! $reload == "false" ]]; then
+ RestartDNS
+ fi
}
main() {
diff --git a/advanced/Scripts/wildcard_regex_converter.sh b/advanced/Scripts/wildcard_regex_converter.sh
deleted file mode 100644
index b4b6b4a1..00000000
--- a/advanced/Scripts/wildcard_regex_converter.sh
+++ /dev/null
@@ -1,28 +0,0 @@
-#!/usr/bin/env bash
-# Pi-hole: A black hole for Internet advertisements
-# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
-# Network-wide ad blocking via your own hardware.
-#
-# Provides an automated migration subroutine to convert Pi-hole v3.x wildcard domains to Pi-hole v4.x regex filters
-#
-# This file is copyright under the latest version of the EUPL.
-# Please see LICENSE file for your rights under this license.
-
-# regexFile set in gravity.sh
-
-wildcardFile="/etc/dnsmasq.d/03-pihole-wildcard.conf"
-
-convert_wildcard_to_regex() {
- if [ ! -f "${wildcardFile}" ]; then
- return
- fi
- local addrlines domains uniquedomains
- # Obtain wildcard domains from old file
- addrlines="$(grep -oE "/.*/" ${wildcardFile})"
- # Strip "/" from domain names and convert "." to regex-compatible "\."
- domains="$(sed 's/\///g;s/\./\\./g' <<< "${addrlines}")"
- # Remove repeated domains (may have been inserted two times due to A and AAAA blocking)
- uniquedomains="$(uniq <<< "${domains}")"
- # Automatically generate regex filters and remove old wildcards file
- awk '{print "(^|\\.)"$0"$"}' <<< "${uniquedomains}" >> "${regexFile:?}" && rm "${wildcardFile}"
-}
diff --git a/advanced/Templates/gravity.db.sql b/advanced/Templates/gravity.db.sql
index 26b030c8..3f696d6d 100644
--- a/advanced/Templates/gravity.db.sql
+++ b/advanced/Templates/gravity.db.sql
@@ -32,7 +32,10 @@ CREATE TABLE adlist
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,
- date_updated INTEGER
+ date_updated INTEGER,
+ number INTEGER NOT NULL DEFAULT 0,
+ invalid_domains INTEGER NOT NULL DEFAULT 0,
+ status INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE adlist_by_group
@@ -54,7 +57,7 @@ CREATE TABLE info
value TEXT NOT NULL
);
-INSERT INTO "info" VALUES('version','13');
+INSERT INTO "info" VALUES('version','15');
CREATE TABLE domain_audit
(
@@ -140,12 +143,10 @@ CREATE VIEW vw_gravity AS SELECT domain, adlist_by_group.group_id AS group_id
LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1);
-CREATE VIEW vw_adlist AS SELECT DISTINCT address, adlist.id AS id
+CREATE VIEW vw_adlist AS SELECT DISTINCT address, id
FROM adlist
- LEFT JOIN adlist_by_group ON adlist_by_group.adlist_id = adlist.id
- LEFT JOIN "group" ON "group".id = adlist_by_group.group_id
- WHERE adlist.enabled = 1 AND (adlist_by_group.group_id IS NULL OR "group".enabled = 1)
- ORDER BY adlist.id;
+ WHERE enabled = 1
+ ORDER BY id;
CREATE TRIGGER tr_domainlist_add AFTER INSERT ON domainlist
BEGIN
diff --git a/advanced/Templates/pihole-FTL.service b/advanced/Templates/pihole-FTL.service
index f0743b49..865e2cd9 100644
--- a/advanced/Templates/pihole-FTL.service
+++ b/advanced/Templates/pihole-FTL.service
@@ -1,4 +1,4 @@
-#!/usr/bin/env bash
+#!/usr/bin/env sh
### BEGIN INIT INFO
# Provides: pihole-FTL
# Required-Start: $remote_fs $syslog $network
@@ -9,11 +9,8 @@
# Description: Enable service provided by pihole-FTL daemon
### END INIT INFO
-FTLUSER=pihole
-PIDFILE=/run/pihole-FTL.pid
-
is_running() {
- pgrep -o "pihole-FTL" > /dev/null 2>&1
+ pgrep -xo "pihole-FTL" > /dev/null
}
@@ -23,27 +20,22 @@ start() {
echo "pihole-FTL is already running"
else
# Touch files to ensure they exist (create if non-existing, preserve if existing)
- touch /var/log/pihole-FTL.log /var/log/pihole.log
- touch /run/pihole-FTL.pid /run/pihole-FTL.port
- touch /etc/pihole/dhcp.leases
- mkdir -p /run/pihole
- mkdir -p /var/log/pihole
- chown pihole:pihole /run/pihole /var/log/pihole
- # Remove possible leftovers from previous pihole-FTL processes
- rm -f /dev/shm/FTL-* 2> /dev/null
- rm /run/pihole/FTL.sock 2> /dev/null
+ mkdir -pm 0755 /run/pihole
+ touch /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases
# Ensure that permissions are set so that pihole-FTL can edit all necessary files
- chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port
- chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null
- chown pihole:pihole /var/log/pihole-FTL.log /var/log/pihole.log
- chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log
+ chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases /run/pihole /etc/pihole
+ chmod 0644 /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole-FTL.log /var/log/pihole.log /etc/pihole/dhcp.leases
+ # Ensure that permissions are set so that pihole-FTL can edit the files. We ignore errors as the file may not (yet) exist
+ chmod -f 0644 /etc/pihole/macvendor.db
# Chown database files to the user FTL runs as. We ignore errors as the files may not (yet) exist
- chown pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db 2> /dev/null
- if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE+eip "$(which pihole-FTL)"; then
- su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER"
+ chown -f pihole:pihole /etc/pihole/pihole-FTL.db /etc/pihole/gravity.db /etc/pihole/macvendor.db
+ # Chown database file permissions so that the pihole group (web interface) can edit the file. We ignore errors as the files may not (yet) exist
+ chmod -f 0664 /etc/pihole/pihole-FTL.db
+ if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,CAP_IPC_LOCK,CAP_CHOWN+eip "/usr/bin/pihole-FTL"; then
+ su -s /bin/sh -c "/usr/bin/pihole-FTL" pihole
else
echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system"
- pihole-FTL
+ /usr/bin/pihole-FTL
fi
echo
fi
@@ -52,20 +44,20 @@ start() {
# Stop the service
stop() {
if is_running; then
- pkill -o pihole-FTL
- for i in {1..5}; do
+ pkill -xo "pihole-FTL"
+ for i in 1 2 3 4 5; do
if ! is_running; then
break
fi
- echo -n "."
+ printf "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed, killing now"
- pkill -o -9 pihole-FTL
+ pkill -xo -9 "pihole-FTL"
exit 1
else
echo "Stopped"
@@ -73,6 +65,8 @@ stop() {
else
echo "Not running"
fi
+ # Cleanup
+ rm -f /run/pihole/FTL.sock /dev/shm/FTL-*
echo
}
@@ -101,7 +95,7 @@ case "$1" in
start
;;
*)
- echo $"Usage: $0 {start|stop|restart|reload|status}"
+ echo "Usage: $0 {start|stop|restart|reload|status}"
exit 1
esac
diff --git a/advanced/Templates/pihole.cron b/advanced/Templates/pihole.cron
index ecd1e808..37724d2e 100644
--- a/advanced/Templates/pihole.cron
+++ b/advanced/Templates/pihole.cron
@@ -26,7 +26,7 @@
# parameter "quiet": don't print messages
00 00 * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole flush once quiet
-@reboot root /usr/sbin/logrotate /etc/pihole/logrotate
+@reboot root /usr/sbin/logrotate --state /var/lib/logrotate/pihole /etc/pihole/logrotate
# Pi-hole: Grab local version and branch every 10 minutes
*/10 * * * * root PATH="$PATH:/usr/sbin:/usr/local/bin/" pihole updatechecker local
diff --git a/advanced/blockingpage.css b/advanced/blockingpage.css
index 9f82a27e..0cc7a65c 100644
--- a/advanced/blockingpage.css
+++ b/advanced/blockingpage.css
@@ -145,7 +145,17 @@ body {
}
/* User is greeted with a splash page when browsing to Pi-hole IP address */
-#splashpage { background: #222; color: rgba(255, 255, 255, 0.7); text-align: center; }
+#splashpage {
+ background: #222;
+ color: rgba(255, 255, 255, 0.7);
+ text-align: center;
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+
#splashpage img { margin: 5px; width: 256px; }
#splashpage b { color: inherit; }
@@ -196,6 +206,26 @@ header #bpAlt label {
display: block;
}
+html, body {
+ height: 100%;
+}
+
+#pihole_card {
+ width: 400px;
+ height: auto;
+ max-width: 400px;
+}
+
+ #pihole_card p, #pihole_card a {
+ font-size: 13pt;
+ text-align: center;
+ }
+
+#pihole_logo_splash {
+ height: auto;
+ width: 100%;
+}
+
/* Click anywhere else on screen to hide #bpAbout */
#bpAboutToggle:checked {
display: block;
@@ -382,12 +412,44 @@ footer {
/* Responsive Content */
@media only screen and (max-width: 500px) {
- h1 a { font-size: 1.8rem; min-width: 170px; }
- footer span::before { content: "Generated "; }
- footer span { display: block; }
+ h1 a {
+ font-size: 1.8rem;
+ min-width: 170px;
+ }
+
+ footer span::before {
+ content: "Generated ";
+ }
+
+ footer span {
+ display: block;
+ }
}
@media only screen and (min-width: 1251px) {
- #bpWrapper, footer { border-radius: 0 0 5px 5px; }
- #bpAbout { border-right-width: 1px; }
+ #bpWrapper, footer {
+ border-radius: 0 0 5px 5px;
+ }
+
+ #bpAbout {
+ border-right-width: 1px;
+ }
+}
+
+@media only screen and (max-width: 400px) {
+ #pihole_card {
+ width: 100%;
+ height: auto;
+ }
+
+ #pihole_card p, #pihole_card a {
+ font-size: 100%;
+ }
+}
+
+@media only screen and (max-width: 256px) {
+ #pihole_logo_splash {
+ width: 90% !important;
+ height: auto;
+ }
}
diff --git a/advanced/index.php b/advanced/index.php
index ca8c35e4..d0c5fc5d 100644
--- a/advanced/index.php
+++ b/advanced/index.php
@@ -58,31 +58,33 @@ if ($serverName === "pi.hole"
// When directly browsing via IP or authorized hostname
// Render splash/landing page based off presence of $landPage file
// Unset variables so as to not be included in $landPage or $splashPage
- unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort);
+ unset($svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt);
// If $landPage file is present
if (is_file(getcwd()."/$landPage")) {
+ unset($serverName, $viewPort); // unset extra variables not to be included in $landpage
include $landPage;
exit();
}
// If $landPage file was not present, Set Splash Page output
- $splashPage = "
+ $splashPage = <<
$viewPort
● $serverName
-
-
+
+
-
-
-
Pi-hole: Your black hole for Internet advertisements
- ";
+EOT;
exit($splashPage);
} elseif ($currentUrlExt === "js") {
// Serve Pi-hole JavaScript for blocked domains requesting JS
diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian
index 6020dfd8..a58b5a88 100644
--- a/advanced/lighttpd.conf.debian
+++ b/advanced/lighttpd.conf.debian
@@ -20,7 +20,6 @@ server.modules = (
"mod_accesslog",
"mod_auth",
"mod_expire",
- "mod_compress",
"mod_redirect",
"mod_setenv",
"mod_rewrite"
@@ -41,26 +40,6 @@ index-file.names = ( "index.php", "index.html", "index.lighttpd.html"
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
-compress.cache-dir = "/var/cache/lighttpd/compress/"
-compress.filetype = (
- "application/json",
- "application/vnd.ms-fontobject",
- "application/xml",
- "font/eot",
- "font/opentype",
- "font/otf",
- "font/ttf",
- "image/bmp",
- "image/svg+xml",
- "image/vnd.microsoft.icon",
- "image/x-icon",
- "text/css",
- "text/html",
- "text/javascript",
- "text/plain",
- "text/xml"
-)
-
mimetype.assign = (
".ico" => "image/x-icon",
".jpeg" => "image/jpeg",
@@ -81,6 +60,10 @@ mimetype.assign = (
".woff2" => "font/woff2"
)
+# Add user chosen options held in external file
+# This uses include_shell instead of an include wildcard for compatibility
+include_shell "cat external.conf 2>/dev/null"
+
# default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
@@ -95,11 +78,6 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY"
)
-
- $HTTP["url"] =~ "\.(eot|otf|tt[cf]|woff2?)$" {
- # Allow Block Page access to local fonts
- setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
- }
}
# Block . files from being served, such as .git, .github, .gitignore
@@ -107,9 +85,12 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("")
}
+# allow teleporter iframe on settings page
+$HTTP["url"] =~ "/teleporter\.php$" {
+ $HTTP["referer"] =~ "/admin/settings\.php" {
+ setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" )
+ }
+}
+
# Default expire header
expire.url = ( "" => "access plus 0 seconds" )
-
-# Add user chosen options held in external file
-# This uses include_shell instead of an include wildcard for compatibility
-include_shell "cat external.conf 2>/dev/null"
diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora
index 8e3bddc4..ad336a93 100644
--- a/advanced/lighttpd.conf.fedora
+++ b/advanced/lighttpd.conf.fedora
@@ -21,7 +21,6 @@ server.modules = (
"mod_expire",
"mod_fastcgi",
"mod_accesslog",
- "mod_compress",
"mod_redirect",
"mod_setenv",
"mod_rewrite"
@@ -42,26 +41,6 @@ index-file.names = ( "index.php", "index.html", "index.lighttpd.html"
url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
-compress.cache-dir = "/var/cache/lighttpd/compress/"
-compress.filetype = (
- "application/json",
- "application/vnd.ms-fontobject",
- "application/xml",
- "font/eot",
- "font/opentype",
- "font/otf",
- "font/ttf",
- "image/bmp",
- "image/svg+xml",
- "image/vnd.microsoft.icon",
- "image/x-icon",
- "text/css",
- "text/html",
- "text/javascript",
- "text/plain",
- "text/xml"
-)
-
mimetype.assign = (
".ico" => "image/x-icon",
".jpeg" => "image/jpeg",
@@ -82,6 +61,10 @@ mimetype.assign = (
".woff2" => "font/woff2"
)
+# Add user chosen options held in external file
+# This uses include_shell instead of an include wildcard for compatibility
+include_shell "cat external.conf 2>/dev/null"
+
# default listening port for IPv6 falls back to the IPv4 port
#include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
#include_shell "/usr/share/lighttpd/create-mime.assign.pl"
@@ -103,11 +86,6 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY"
)
-
- $HTTP["url"] =~ "\.(eot|otf|tt[cf]|woff2?)$" {
- # Allow Block Page access to local fonts
- setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
- }
}
# Block . files from being served, such as .git, .github, .gitignore
@@ -115,9 +93,12 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("")
}
+# allow teleporter iframe on settings page
+$HTTP["url"] =~ "/teleporter\.php$" {
+ $HTTP["referer"] =~ "/admin/settings\.php" {
+ setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" )
+ }
+}
+
# Default expire header
expire.url = ( "" => "access plus 0 seconds" )
-
-# Add user chosen options held in external file
-# This uses include_shell instead of an include wildcard for compatibility
-include_shell "cat external.conf 2>/dev/null"
diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh
index 4ab5c0e2..babb8213 100755
--- a/automated install/basic-install.sh
+++ b/automated install/basic-install.sh
@@ -21,8 +21,8 @@
# instead of continuing the installation with something broken
set -e
-# Set PATH to a usual default to assure that all basic commands are available.
-# When using "su" an uncomplete PATH could be passed: https://github.com/pi-hole/pi-hole/issues/3209
+# Append common folders to the PATH to ensure that all basic commands are available.
+# When using "su" an incomplete PATH could be passed: https://github.com/pi-hole/pi-hole/issues/3209
export PATH+=':/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
######## VARIABLES #########
@@ -34,37 +34,41 @@ export PATH+=':/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
# List of supported DNS servers
DNS_SERVERS=$(cat << EOM
-Google (ECS);8.8.8.8;8.8.4.4;2001:4860:4860:0:0:0:0:8888;2001:4860:4860:0:0:0:0:8844
+Google (ECS, DNSSEC);8.8.8.8;8.8.4.4;2001:4860:4860:0:0:0:0:8888;2001:4860:4860:0:0:0:0:8844
OpenDNS (ECS, DNSSEC);208.67.222.222;208.67.220.220;2620:119:35::35;2620:119:53::53
Level3;4.2.2.1;4.2.2.2;;
Comodo;8.26.56.26;8.20.247.20;;
-DNS.WATCH;84.200.69.80;84.200.70.40;2001:1608:10:25:0:0:1c04:b12f;2001:1608:10:25:0:0:9249:d69b
+DNS.WATCH (DNSSEC);84.200.69.80;84.200.70.40;2001:1608:10:25:0:0:1c04:b12f;2001:1608:10:25:0:0:9249:d69b
Quad9 (filtered, DNSSEC);9.9.9.9;149.112.112.112;2620:fe::fe;2620:fe::9
Quad9 (unfiltered, no DNSSEC);9.9.9.10;149.112.112.10;2620:fe::10;2620:fe::fe:10
-Quad9 (filtered + ECS);9.9.9.11;149.112.112.11;2620:fe::11;2620:fe::fe:11
-Cloudflare;1.1.1.1;1.0.0.1;2606:4700:4700::1111;2606:4700:4700::1001
+Quad9 (filtered, ECS, DNSSEC);9.9.9.11;149.112.112.11;2620:fe::11;2620:fe::fe:11
+Cloudflare (DNSSEC);1.1.1.1;1.0.0.1;2606:4700:4700::1111;2606:4700:4700::1001
EOM
)
# Location for final installation log storage
-installLogLoc=/etc/pihole/install.log
+installLogLoc="/etc/pihole/install.log"
# This is an important file as it contains information specific to the machine it's being installed on
-setupVars=/etc/pihole/setupVars.conf
+setupVars="/etc/pihole/setupVars.conf"
# Pi-hole uses lighttpd as a Web server, and this is the config file for it
-# shellcheck disable=SC2034
-lighttpdConfig=/etc/lighttpd/lighttpd.conf
+lighttpdConfig="/etc/lighttpd/lighttpd.conf"
# This is a file used for the colorized output
-coltable=/opt/pihole/COL_TABLE
+coltable="/opt/pihole/COL_TABLE"
# Root of the web server
webroot="/var/www/html"
-# We store several other directories and
+
+# We clone (or update) two git repositories during the install. This helps to make sure that we always have the latest versions of the relevant files.
+# AdminLTE is used to set up the Web admin interface.
+# Pi-hole contains various setup scripts and files which are critical to the installation.
+# Search for "PI_HOLE_LOCAL_REPO" in this file to see all such scripts.
+# Two notable scripts are gravity.sh (used to generate the HOSTS file) and advanced/Scripts/webpage.sh (used to install the Web admin interface)
webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git"
webInterfaceDir="${webroot}/admin"
piholeGitUrl="https://github.com/pi-hole/pi-hole.git"
PI_HOLE_LOCAL_REPO="/etc/.pihole"
-# These are the names of pi-holes files, stored in an array
+# List of pihole scripts, stored in an array
PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version gravity uninstall webpage)
# This directory is where the Pi-hole scripts will be installed
PI_HOLE_INSTALL_DIR="/opt/pihole"
@@ -76,11 +80,10 @@ if [ -z "$useUpdateVars" ]; then
fi
adlistFile="/etc/pihole/adlists.list"
-# Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until
-# this script can run
+# Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until this script can run
IPV4_ADDRESS=${IPV4_ADDRESS}
IPV6_ADDRESS=${IPV6_ADDRESS}
-# By default, query logging is enabled and the dashboard is set to be installed
+# Give settings their default values. These may be changed by prompts later in the script.
QUERY_LOGGING=true
INSTALL_WEB_INTERFACE=true
PRIVACY_LEVEL=0
@@ -90,25 +93,9 @@ if [ -z "${USER}" ]; then
USER="$(id -un)"
fi
-
-# Check if we are running on a real terminal and find the rows and columns
-# If there is no real terminal, we will default to 80x24
-if [ -t 0 ] ; then
- screen_size=$(stty size)
-else
- screen_size="24 80"
-fi
-# Set rows variable to contain first number
-printf -v rows '%d' "${screen_size%% *}"
-# Set columns variable to contain second number
-printf -v columns '%d' "${screen_size##* }"
-
-# Divide by two so the dialogs take up half of the screen, which looks nice.
-r=$(( rows / 2 ))
-c=$(( columns / 2 ))
-# Unless the screen is tiny
-r=$(( r < 20 ? 20 : r ))
-c=$(( c < 70 ? 70 : c ))
+# whiptail dialog dimensions: 20 rows and 70 chars width assures to fit on small screens and is known to hold all content.
+r=20
+c=70
######## Undocumented Flags. Shhh ########
# These are undocumented flags; some of which we can use when repairing an installation
@@ -171,9 +158,8 @@ show_ascii_berry() {
}
is_command() {
- # Checks for existence of string passed in as only function argument.
- # Exit value of 0 when exists, 1 if not exists. Value is the result
- # of the `command` shell built-in call.
+ # Checks to see if the given command (passed as a string argument) exists on the system.
+ # The function returns 0 (success) if the command exists, and 1 if it doesn't.
local check_command="$1"
command -v "${check_command}" >/dev/null 2>&1
@@ -184,21 +170,21 @@ os_check() {
# This function gets a list of supported OS versions from a TXT record at versions.pi-hole.net
# and determines whether or not the script is running on one of those systems
local remote_os_domain valid_os valid_version valid_response detected_os detected_version display_warning cmdResult digReturnCode response
- remote_os_domain="versions.pi-hole.net"
+ remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"versions.pi-hole.net"}
detected_os=$(grep "\bID\b" /etc/os-release | cut -d '=' -f2 | tr -d '"')
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
- cmdResult="$(dig +short -t txt ${remote_os_domain} @ns1.pi-hole.net 2>&1; echo $?)"
- #Get the return code of the previous command (last line)
+ cmdResult="$(dig +short -t txt "${remote_os_domain}" @ns1.pi-hole.net 2>&1; echo $?)"
+ # Gets the return code of the previous command (last line)
digReturnCode="${cmdResult##*$'\n'}"
if [ ! "${digReturnCode}" == "0" ]; then
valid_response=false
else
- # Dig returned 0 code, so get the actual response, and loop through it to determine if the detected variables above are valid
+ # Dig returned 0 (success), so get the actual response, and loop through it to determine if the detected variables above are valid
response="${cmdResult%%$'\n'*}"
- # If the value of ${result} is a single 0, then this is the return code, not the response. Response is blank
+ # If the value of ${response} is a single 0, then this is the return code, not an actual response.
if [ "${response}" == 0 ]; then
valid_response=false
fi
@@ -209,6 +195,7 @@ os_check() {
distro_part="${distro_and_versions%%=*}"
versions_part="${distro_and_versions##*=}"
+ # If the distro part is a (case-insensistive) substring of the computer OS
if [[ "${detected_os^^}" =~ ${distro_part^^} ]]; then
valid_os=true
IFS="," read -r -a supportedVer <<<"${versions_part}"
@@ -273,90 +260,41 @@ os_check() {
}
# Compatibility
-distro_check() {
-# If apt-get is installed, then we know it's part of the Debian family
+package_manager_detect() {
+# First check to see if apt-get is installed.
if is_command apt-get ; then
# Set some global variables here
- # We don't set them earlier since the family might be Red Hat, so these values would be different
+ # We don't set them earlier since the installed package manager might be rpm, so these values would be different
PKG_MANAGER="apt-get"
# A variable to store the command used to update the package cache
UPDATE_PKG_CACHE="${PKG_MANAGER} update"
- # An array for something...
+ # The command we will use to actually install packages
PKG_INSTALL=("${PKG_MANAGER}" -qq --no-install-recommends install)
- # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE
+ # grep -c will return 1 if there are no matches. This is an acceptable condition, so we OR TRUE to prevent set -e exiting the script.
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 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" "${r}" "${c}"; then
- printf " %b Aborting installation: Dependencies could not be installed.\\n" "${CROSS}"
- exit 1 # exit the installer
- else
- printf " %b Enabling universe package repository for Ubuntu Bionic\\n" "${INFO}"
- 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
- # Update package cache. This is required already here to assure apt-cache calls have package lists available.
+ # Update package cache
update_package_cache || exit 1
- # Debian 7 doesn't have iproute2 so check if it's available first
- if apt-cache show iproute2 > /dev/null 2>&1; then
- iproute_pkg="iproute2"
- # Otherwise, check if iproute is available
- elif apt-cache show iproute > /dev/null 2>&1; then
- iproute_pkg="iproute"
- # Else print error and exit
- else
- printf " %b Aborting installation: iproute2 and iproute packages were not found in APT repository.\\n" "${CROSS}"
- exit 1
- fi
# Check for and determine version number (major and minor) of current php install
+ local phpVer="php"
if is_command php ; then
printf " %b Existing PHP installation detected : PHP version %s\\n" "${INFO}" "$(php <<< "")"
printf -v phpInsMajor "%d" "$(php <<< "")"
printf -v phpInsMinor "%d" "$(php <<< "")"
- # Is installed php version 7.0 or greater
- if [ "${phpInsMajor}" -ge 7 ]; then
- phpInsNewer=true
- fi
- fi
- # 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 apt-cache show php > /dev/null 2>&1; then
- phpVer="php"
- # Else fall back on the php5 package if it's there
- elif apt-cache show php5 > /dev/null 2>&1; then
- phpVer="php5"
- # Else print error and exit
- else
- printf " %b Aborting installation: No PHP packages were found in APT repository.\\n" "${CROSS}"
- exit 1
- fi
- else
- # Newer php is installed, its common, cgi & sqlite counterparts are deps
phpVer="php$phpInsMajor.$phpInsMinor"
fi
- # We also need the correct version for `php-sqlite` (which differs across distros)
- if apt-cache show "${phpVer}-sqlite3" > /dev/null 2>&1; then
- phpSqlite="sqlite3"
- elif apt-cache show "${phpVer}-sqlite" > /dev/null 2>&1; then
- phpSqlite="sqlite"
- else
- printf " %b Aborting installation: No SQLite PHP module was found in APT repository.\\n" "${CROSS}"
- exit 1
+ # Packages required to perfom the os_check (stored as an array)
+ OS_CHECK_DEPS=(grep dnsutils)
+ # Packages required to run this install script (stored as an array)
+ INSTALLER_DEPS=(git iproute2 whiptail ca-certificates)
+ # Packages required to run Pi-hole (stored as an array)
+ PIHOLE_DEPS=(cron curl iputils-ping lsof psmisc sudo unzip idn2 sqlite3 libcap2-bin dns-root-data libcap2)
+ # Packages required for the Web admin interface (stored as an array)
+ # It's useful to separate this from Pi-hole, since the two repos are also setup separately
+ PIHOLE_WEB_DEPS=(lighttpd "${phpVer}-common" "${phpVer}-cgi" "${phpVer}-sqlite3" "${phpVer}-xml" "${phpVer}-intl")
+ # Prior to PHP8.0, JSON functionality is provided as dedicated module, required by Pi-hole AdminLTE: https://www.php.net/manual/json.installation.php
+ if [[ -z "${phpInsMajor}" || "${phpInsMajor}" -lt 8 ]]; then
+ PIHOLE_WEB_DEPS+=("${phpVer}-json")
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=(dhcpcd5 git "${iproute_pkg}" whiptail dnsutils)
- # Pi-hole itself has several dependencies that also need to be installed
- PIHOLE_DEPS=(cron curl iputils-ping lsof netcat psmisc sudo unzip wget idn2 sqlite3 libcap2-bin dns-root-data 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}" "${phpVer}-xml" "${phpVer}-json" "${phpVer}-intl")
# The Web server user,
LIGHTTPD_USER="www-data"
# group,
@@ -364,24 +302,23 @@ if is_command apt-get ; then
# and config file
LIGHTTPD_CFG="lighttpd.conf.debian"
- # A function to check...
+ # This function waits for dpkg to unlock, which signals that the previous apt-get command has finished.
test_dpkg_lock() {
- # An iterator used for counting loop iterations
i=0
# fuser is a program to show which processes use the named files, sockets, or filesystems
- # So while the command is true
- while fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
- # Wait half a second
+ # So while the lock is held,
+ while fuser /var/lib/dpkg/lock >/dev/null 2>&1
+ do
+ # we wait half a second,
sleep 0.5
- # and increase the iterator
+ # increase the iterator,
((i=i+1))
done
- # Always return success, since we only return if there is no
- # lock (anymore)
+ # and then report success once dpkg is unlocked.
return 0
}
-# If apt-get is not found, check for rpm to see if it's a Red Hat family OS
+# If apt-get is not found, check for rpm.
elif is_command rpm ; then
# Then check if dnf or yum is the package manager
if is_command dnf ; then
@@ -390,107 +327,110 @@ elif is_command rpm ; then
PKG_MANAGER="yum"
fi
+ # These variable names match the ones for apt-get. See above for an explanation of what they are for.
PKG_INSTALL=("${PKG_MANAGER}" install -y)
PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l"
- INSTALLER_DEPS=(git iproute newt procps-ng which chkconfig bind-utils)
- PIHOLE_DEPS=(cronie curl findutils nmap-ncat sudo unzip libidn2 psmisc sqlite libcap lsof)
+ OS_CHECK_DEPS=(grep bind-utils)
+ INSTALLER_DEPS=(git iproute newt procps-ng which chkconfig ca-certificates)
+ PIHOLE_DEPS=(cronie curl findutils sudo unzip libidn2 psmisc sqlite libcap lsof)
PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo php-xml php-json php-intl)
LIGHTTPD_USER="lighttpd"
LIGHTTPD_GROUP="lighttpd"
LIGHTTPD_CFG="lighttpd.conf.fedora"
- # If the host OS is Fedora,
- if grep -qiE 'fedora|fedberry' /etc/redhat-release; then
- # all required packages should be available by default with the latest fedora release
- : # continue
- # or if host OS is CentOS,
- elif grep -qiE 'centos|scientific' /etc/redhat-release; then
- # Pi-Hole currently supports CentOS 7+ with PHP7+
- SUPPORTED_CENTOS_VERSION=7
- SUPPORTED_CENTOS_PHP_VERSION=7
- # Check current CentOS major release version
- CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.?)' /etc/redhat-release)
- # Check if CentOS version is supported
- if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then
- printf " %b CentOS %s is not supported.\\n" "${CROSS}" "${CURRENT_CENTOS_VERSION}"
- printf " Please update to CentOS release %s or later.\\n" "${SUPPORTED_CENTOS_VERSION}"
- # exit the installer
- exit
- fi
- # php-json is not required on CentOS 7 as it is already compiled into php
- # verifiy via `php -m | grep json`
- if [[ $CURRENT_CENTOS_VERSION -eq 7 ]]; then
- # create a temporary array as arrays are not designed for use as mutable data structures
- CENTOS7_PIHOLE_WEB_DEPS=()
- for i in "${!PIHOLE_WEB_DEPS[@]}"; do
- if [[ ${PIHOLE_WEB_DEPS[i]} != "php-json" ]]; then
- CENTOS7_PIHOLE_WEB_DEPS+=( "${PIHOLE_WEB_DEPS[i]}" )
- fi
- done
- # re-assign the clean dependency array back to PIHOLE_WEB_DEPS
- PIHOLE_WEB_DEPS=("${CENTOS7_PIHOLE_WEB_DEPS[@]}")
- unset CENTOS7_PIHOLE_WEB_DEPS
- fi
- # CentOS requires the EPEL repository to gain access to Fedora packages
- EPEL_PKG="epel-release"
- rpm -q ${EPEL_PKG} &> /dev/null || rc=$?
- if [[ $rc -ne 0 ]]; then
- printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}"
- "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null
- printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}"
- fi
-
- # 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)
- 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
- else
- REMI_PKG="remi-release"
- REMI_REPO="remi-php72"
- 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
- # 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
- else
- printf " %b Enabling Remi's RPM repository (https://rpms.remirepo.net)\\n" "${INFO}"
- "${PKG_INSTALL[@]}" "https://rpms.remirepo.net/enterprise/${REMI_PKG}-$(rpm -E '%{rhel}').rpm" &> /dev/null
- # enable the PHP 7 repository via yum-config-manager (provided by yum-utils)
- "${PKG_INSTALL[@]}" "yum-utils" &> /dev/null
- yum-config-manager --enable ${REMI_REPO} &> /dev/null
- printf " %b Remi's RPM repository has been enabled for PHP7\\n" "${TICK}"
- # trigger an install/update of PHP to ensure previous version of PHP is updated from REMI
- if "${PKG_INSTALL[@]}" "php-cli" &> /dev/null; then
- printf " %b PHP7 installed/updated via Remi's RPM repository\\n" "${TICK}"
- else
- printf " %b There was a problem updating to PHP7 via Remi's RPM repository\\n" "${CROSS}"
- exit 1
- fi
- fi
- fi
- 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
- printf " %b Aborting installation due to unsupported RPM based distribution\\n" "${CROSS}"
- exit # exit the installer
- else
- printf " %b Continuing installation with unsupported RPM based distribution\\n" "${INFO}"
- fi
- fi
# If neither apt-get or yum/dnf package managers were found
else
- # it's not an OS we can support,
- printf " %b OS distribution not supported\\n" "${CROSS}"
+ # we cannot install required packages
+ printf " %b No supported package manager found\\n" "${CROSS}"
# so exit the installer
exit
fi
}
+select_rpm_php(){
+# If the host OS is Fedora,
+if grep -qiE 'fedora|fedberry' /etc/redhat-release; then
+ # all required packages should be available by default with the latest fedora release
+ : # continue
+# or if host OS is CentOS,
+elif grep -qiE 'centos|scientific' /etc/redhat-release; then
+ # Pi-Hole currently supports CentOS 7+ with PHP7+
+ SUPPORTED_CENTOS_VERSION=7
+ SUPPORTED_CENTOS_PHP_VERSION=7
+ # Check current CentOS major release version
+ CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.?)' /etc/redhat-release)
+ # Check if CentOS version is supported
+ if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then
+ printf " %b CentOS %s is not supported.\\n" "${CROSS}" "${CURRENT_CENTOS_VERSION}"
+ printf " Please update to CentOS release %s or later.\\n" "${SUPPORTED_CENTOS_VERSION}"
+ # exit the installer
+ exit
+ fi
+ # php-json is not required on CentOS 7 as it is already compiled into php
+ # verifiy via `php -m | grep json`
+ if [[ $CURRENT_CENTOS_VERSION -eq 7 ]]; then
+ # create a temporary array as arrays are not designed for use as mutable data structures
+ CENTOS7_PIHOLE_WEB_DEPS=()
+ for i in "${!PIHOLE_WEB_DEPS[@]}"; do
+ if [[ ${PIHOLE_WEB_DEPS[i]} != "php-json" ]]; then
+ CENTOS7_PIHOLE_WEB_DEPS+=( "${PIHOLE_WEB_DEPS[i]}" )
+ fi
+ done
+ # re-assign the clean dependency array back to PIHOLE_WEB_DEPS
+ PIHOLE_WEB_DEPS=("${CENTOS7_PIHOLE_WEB_DEPS[@]}")
+ unset CENTOS7_PIHOLE_WEB_DEPS
+ fi
+ # CentOS requires the EPEL repository to gain access to Fedora packages
+ EPEL_PKG="epel-release"
+ rpm -q ${EPEL_PKG} &> /dev/null || rc=$?
+ if [[ $rc -ne 0 ]]; then
+ printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}"
+ "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null
+ printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}"
+ fi
+
+ # 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)
+ 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
+ else
+ REMI_PKG="remi-release"
+ REMI_REPO="remi-php72"
+ 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
+ # 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
+ else
+ printf " %b Enabling Remi's RPM repository (https://rpms.remirepo.net)\\n" "${INFO}"
+ "${PKG_INSTALL[@]}" "https://rpms.remirepo.net/enterprise/${REMI_PKG}-$(rpm -E '%{rhel}').rpm" &> /dev/null
+ # enable the PHP 7 repository via yum-config-manager (provided by yum-utils)
+ "${PKG_INSTALL[@]}" "yum-utils" &> /dev/null
+ yum-config-manager --enable ${REMI_REPO} &> /dev/null
+ printf " %b Remi's RPM repository has been enabled for PHP7\\n" "${TICK}"
+ # trigger an install/update of PHP to ensure previous version of PHP is updated from REMI
+ if "${PKG_INSTALL[@]}" "php-cli" &> /dev/null; then
+ printf " %b PHP7 installed/updated via Remi's RPM repository\\n" "${TICK}"
+ else
+ printf " %b There was a problem updating to PHP7 via Remi's RPM repository\\n" "${CROSS}"
+ exit 1
+ fi
+ fi
+ fi # 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
+ printf " %b Aborting installation due to unsupported RPM based distribution\\n" "${CROSS}"
+ exit
+ else
+ printf " %b Continuing installation with unsupported RPM based distribution\\n" "${INFO}"
+ fi
+fi
+fi
+}
+
# A function for checking if a directory is a git repository
is_repo() {
# Use a named, local variable instead of the vague $1, which is the first argument passed to this function
@@ -540,8 +480,9 @@ make_repo() {
# Check current branch. If it is master, then reset to the latest available tag.
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD)
- if [[ "${curBranch}" == "master" ]]; then #If we're calling make_repo() then it should always be master, we may not need to check.
- git reset --hard "$(git describe --abbrev=0 --tags)" || return $?
+ if [[ "${curBranch}" == "master" ]]; then
+ # If we're calling make_repo() then it should always be master, we may not need to check.
+ git reset --hard "$(git describe --abbrev=0 --tags)" || return $?
fi
# Show a colored message showing it's status
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
@@ -573,7 +514,7 @@ update_repo() {
git stash --all --quiet &> /dev/null || true # Okay for stash failure
git clean --quiet --force -d || true # Okay for already clean directory
# Pull the latest commits
- git pull --quiet &> /dev/null || return $?
+ git pull --no-rebase --quiet &> /dev/null || return $?
# Check current branch. If it is master, then reset to the latest available tag.
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD)
@@ -589,7 +530,7 @@ update_repo() {
return 0
}
-# A function that combines the functions previously made
+# A function that combines the previous git functions to update or clone a repo
getGitFiles() {
# Setup named variables for the git repos
# We need the directory
@@ -613,9 +554,8 @@ getGitFiles() {
# Attempt to make the repository, showing an error on failure
make_repo "${directory}" "${remoteRepo}" || { printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
fi
- # echo a blank line
echo ""
- # and return success?
+ # Success via one of the two branches, as the commands would exit if they failed.
return 0
}
@@ -637,7 +577,7 @@ resetRepo() {
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Return to where we came from
popd &> /dev/null || return 1
- # Returning success anyway?
+ # Function succeeded, as "git reset" would have triggered a return earlier if it failed
return 0
}
@@ -684,12 +624,20 @@ welcomeDialogs() {
whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\\n\\nThe Pi-hole is free, but powered by your donations: https://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.
+ if whiptail --defaultno --backtitle "Initiating network interface" --title "Static IP Needed" --yesno "\\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}"
+IMPORTANT: If you have not already done so, you must ensure that this device has a static IP. Either through DHCP reservation, or by manually assigning one. Depending on your operating system, there are many ways to achieve this.
+
+Choose yes to indicate that you have understood this message, and wish to continue" "${r}" "${c}"; then
+#Nothing to do, continue
+ echo
+else
+ printf " %b Installer exited at static IP message.\\n" "${INFO}"
+ exit 1
+fi
}
-# A function that let's the user pick an interface to use with Pi-hole
+# A function that lets the user pick an interface to use with Pi-hole
chooseInterface() {
# Turn the available interfaces into an array so it can be used with a whiptail dialog
local interfacesArray=()
@@ -713,7 +661,7 @@ chooseInterface() {
else
# While reading through the available interfaces
while read -r line; do
- # use a variable to set the option as OFF to begin with
+ # Use a variable to set the option as OFF to begin with
mode="OFF"
# If it's the first loop,
if [[ "${firstLoop}" -eq 1 ]]; then
@@ -747,9 +695,9 @@ chooseInterface() {
testIPv6() {
# first will contain fda2 (ULA)
printf -v first "%s" "${1%%:*}"
- # value1 will contain 253 which is the decimal value corresponding to 0xfd
+ # value1 will contain 253 which is the decimal value corresponding to 0xFD
value1=$(( (0x$first)/256 ))
- # will contain 162 which is the decimal value corresponding to 0xa2
+ # value2 will contain 162 which is the decimal value corresponding to 0xA2
value2=$(( (0x$first)%256 ))
# the ULA test is testing for fc00::/7 according to RFC 4193
if (( (value1&254)==252 )); then
@@ -768,9 +716,8 @@ testIPv6() {
fi
}
-# A dialog for showing the user about IPv6 blocking
-useIPv6dialog() {
- # Determine the IPv6 address used for blocking
+find_IPv6_information() {
+ # Detects IPv6 address used for communication to WAN addresses.
IPV6_ADDRESSES=($(ip -6 address | grep 'scope global' | awk '{print $2}'))
# For each address in the array above, determine the type of IPv6 address it is
@@ -779,8 +726,9 @@ useIPv6dialog() {
result=$(testIPv6 "$i")
# If it's a ULA address, use it and store it as a global variable
[[ "${result}" == "ULA" ]] && ULA_ADDRESS="${i%/*}"
- # If it's a GUA address, we can still use it si store it as a global variable
+ # If it's a GUA address, use it and store it as a global variable
[[ "${result}" == "GUA" ]] && GUA_ADDRESS="${i%/*}"
+ # Else if it's a Link-local address, we cannot use it, so just continue
done
# Determine which address to be used: Prefer ULA over GUA or don't use any if none found
@@ -789,76 +737,36 @@ useIPv6dialog() {
# set the IPv6 address to the ULA address
IPV6_ADDRESS="${ULA_ADDRESS}"
# Show this info to the user
- printf " %b Found IPv6 ULA address, using it for blocking IPv6 ads\\n" "${INFO}"
+ printf " %b Found IPv6 ULA address\\n" "${INFO}"
# Otherwise, if the GUA_ADDRESS has a value,
elif [[ ! -z "${GUA_ADDRESS}" ]]; then
# Let the user know
- printf " %b Found IPv6 GUA address, using it for blocking IPv6 ads\\n" "${INFO}"
+ printf " %b Found IPv6 GUA address\\n" "${INFO}"
# And assign it to the global variable
IPV6_ADDRESS="${GUA_ADDRESS}"
# If none of those work,
else
- # explain that IPv6 blocking will not be used
- printf " %b Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled\\n" "${INFO}"
+ printf " %b Unable to find IPv6 ULA/GUA address\\n" "${INFO}"
# So set the variable to be empty
IPV6_ADDRESS=""
fi
-
- # 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}"
- fi
}
-# A function to check if we should use IPv4 and/or IPv6 for blocking ads
-use4andor6() {
- # Named local variables
- local useIPv4
- local useIPv6
- # Let user choose IPv4 and/or IPv6 via a checklist
- cmd=(whiptail --separate-output --checklist "Select Protocols (press space to toggle selection)" "${r}" "${c}" 2)
- # In an array, show the options available:
- # IPv4 (on by default)
- options=(IPv4 "Block ads over IPv4" on
- # or IPv6 (on by default if available)
- IPv6 "Block ads over IPv6" on)
- # In a variable, show the choices available; exit if Cancel is selected
- choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
- # For each choice available,
- for choice in ${choices}
- do
- # Set the values to true
- case ${choice} in
- IPv4 ) useIPv4=true;;
- IPv6 ) useIPv6=true;;
- esac
- done
- # If IPv4 is to be used,
- if [[ "${useIPv4}" ]]; then
- # Run our function to get the information we need
- find_IPv4_information
- getStaticIPv4Settings
- setStaticIPv4
- fi
- # If IPv6 is to be used,
- if [[ "${useIPv6}" ]]; then
- # Run our function to get this information
- useIPv6dialog
- fi
+# A function to collect IPv4 and IPv6 information of the device
+collect_v4andv6_information() {
+ find_IPv4_information
# Echo the information to the user
printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}"
- printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}"
- # If neither protocol is selected,
- if [[ ! "${useIPv4}" ]] && [[ ! "${useIPv6}" ]]; then
- # Show an error in red
- printf " %bError: Neither IPv4 or IPv6 selected%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"
- # and exit with an error
- exit 1
+ # if `dhcpcd` is used offer to set this as static IP for the device
+ if [[ -f "/etc/dhcpcd.conf" ]]; then
+ # configure networking via dhcpcd
+ getStaticIPv4Settings
+ setDHCPCD
fi
+ find_IPv6_information
+ printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}"
}
-#
getStaticIPv4Settings() {
# Local, named variables
local ipSettingsCorrect
@@ -905,9 +813,9 @@ It is also possible to use a DHCP reservation, but if you are going to do that,
fi
}
-# configure networking via dhcpcd
+# Configure networking via dhcpcd
setDHCPCD() {
- # check if the IP is already in the file
+ # Check if the IP is already in the file
if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then
printf " %b Static IP already configured\\n" "${INFO}"
# If it's not,
@@ -925,107 +833,24 @@ setDHCPCD() {
fi
}
-# configure networking ifcfg-xxxx file found at /etc/sysconfig/network-scripts/
-# this function requires the full path of an ifcfg file passed as an argument
-setIFCFG() {
- # Local, named variables
- local IFCFG_FILE
- local IPADDR
- local CIDR
- IFCFG_FILE=$1
- printf -v IPADDR "%s" "${IPV4_ADDRESS%%/*}"
- # check if the desired IP is already set
- if grep -Eq "${IPADDR}(\\b|\\/)" "${IFCFG_FILE}"; then
- printf " %b Static IP already configured\\n" "${INFO}"
- # Otherwise,
- else
- # Put the IP in variables without the CIDR notation
- printf -v CIDR "%s" "${IPV4_ADDRESS##*/}"
- # Backup existing interface configuration:
- cp -p "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig
- # Build Interface configuration file using the GLOBAL variables we have
- {
- echo "# Configured via Pi-hole installer"
- echo "DEVICE=$PIHOLE_INTERFACE"
- echo "BOOTPROTO=none"
- echo "ONBOOT=yes"
- echo "IPADDR=$IPADDR"
- echo "PREFIX=$CIDR"
- echo "GATEWAY=$IPv4gw"
- echo "DNS1=$PIHOLE_DNS_1"
- 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,
- if is_command nmcli && nmcli general status &> /dev/null; then
- # Tell NetworkManagler to read our new sysconfig file
- nmcli con load "${IFCFG_FILE}" > /dev/null
- fi
- # Show a warning that the user may need to restart
- printf " %b Set IP address to %s\\n You may need to restart after the install is complete\\n" "${TICK}" "${IPV4_ADDRESS%%/*}"
- fi
-}
-
-setStaticIPv4() {
- # Local, named variables
- local IFCFG_FILE
- local CONNECTION_NAME
-
- # If a static interface is already configured, we are done.
- if [[ -r "/etc/sysconfig/network/ifcfg-${PIHOLE_INTERFACE}" ]]; then
- if grep -q '^BOOTPROTO=.static.' "/etc/sysconfig/network/ifcfg-${PIHOLE_INTERFACE}"; then
- return 0
- fi
- fi
- # For the Debian family, if dhcpcd.conf exists,
- if [[ -f "/etc/dhcpcd.conf" ]]; then
- # configure networking via dhcpcd
- setDHCPCD
- return 0
- fi
- # If a DHCPCD config file was not found, check for an ifcfg config file based on interface name
- if [[ -f "/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}" ]];then
- # If it exists,
- IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}
- setIFCFG "${IFCFG_FILE}"
- return 0
- fi
- # if an ifcfg config does not exists for the interface name, try the connection name via network manager
- if is_command nmcli && nmcli general status &> /dev/null; then
- CONNECTION_NAME=$(nmcli dev show "${PIHOLE_INTERFACE}" | grep 'GENERAL.CONNECTION' | cut -d: -f2 | sed 's/^System//' | xargs | tr ' ' '_')
- if [[ -f "/etc/sysconfig/network-scripts/ifcfg-${CONNECTION_NAME}" ]];then
- # If it exists,
- IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${CONNECTION_NAME}
- setIFCFG "${IFCFG_FILE}"
- return 0
- fi
- fi
- # If previous conditions failed, show an error and exit
- printf " %b Warning: Unable to locate configuration file to set static IPv4 address\\n" "${INFO}"
- exit 1
-}
-
# Check an IP address to see if it is a valid one
valid_ip() {
# Local, named variables
local ip=${1}
local stat=1
- # One IPv4 element is 8bit: 0 - 256
+ # Regex matching one IPv4 component, i.e. an integer from 0 to 255.
+ # See https://tools.ietf.org/html/rfc1340
local ipv4elem="(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]?|0)";
- # optional port number starting '#' with range of 1-65536
- local portelem="(#([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-6]))?"
- # build a full regex string from the above parts
+ # Regex matching an optional port (starting with '#') range of 1-65536
+ local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?";
+ # Build a full IPv4 regex from the above subexpressions
local regex="^${ipv4elem}\.${ipv4elem}\.${ipv4elem}\.${ipv4elem}${portelem}$"
+ # Evaluate the regex, and return the result
[[ $ip =~ ${regex} ]]
stat=$?
- # Return the exit code
return "${stat}"
}
@@ -1033,19 +858,19 @@ valid_ip6() {
local ip=${1}
local stat=1
- # One IPv6 element is 16bit: 0000 - FFFF
+ # Regex matching one IPv6 element, i.e. a hex value from 0000 to FFFF
local ipv6elem="[0-9a-fA-F]{1,4}"
- # CIDR for IPv6 is 1- 128 bit
+ # Regex matching an IPv6 CIDR, i.e. 1 to 128
local v6cidr="(\\/([1-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8])){0,1}"
- # optional port number starting '#' with range of 1-65536
- local portelem="(#([1-9]|[1-8][0-9]|9[0-9]|[1-8][0-9]{2}|9[0-8][0-9]|99[0-9]|[1-8][0-9]{3}|9[0-8][0-9]{2}|99[0-8][0-9]|999[0-9]|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-6]))?"
- # build a full regex string from the above parts
+ # Regex matching an optional port (starting with '#') range of 1-65536
+ local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?";
+ # Build a full IPv6 regex from the above subexpressions
local regex="^(((${ipv6elem}))*((:${ipv6elem}))*::((${ipv6elem}))*((:${ipv6elem}))*|((${ipv6elem}))((:${ipv6elem})){7})${v6cidr}${portelem}$"
+ # Evaluate the regex, and return the result
[[ ${ip} =~ ${regex} ]]
stat=$?
- # Return the exit code
return "${stat}"
}
@@ -1057,7 +882,7 @@ setDNS() {
# In an array, list the available upstream providers
DNSChooseOptions=()
local DNSServerCount=0
- # Save the old Internal Field Separator in a variable
+ # Save the old Internal Field Separator in a variable,
OIFS=$IFS
# and set the new one to newline
IFS=$'\n'
@@ -1078,23 +903,22 @@ setDNS() {
# 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 \
"${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \
- # exit if Cancel is selected
+ # Exit if the user selects "Cancel"
{ printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
- # Depending on the user's choice, set the GLOBAl variables to the IP of the respective provider
+ # Depending on the user's choice, set the GLOBAL variables to the IP of the respective provider
if [[ "${DNSchoices}" == "Custom" ]]
then
- # Until the DNS settings are selected,
+ # Loop until we have a valid DNS setting
until [[ "${DNSSettingsCorrect}" = True ]]; do
- #
+ # Signal value, to be used if the user inputs an invalid IP address
strInvalid="Invalid"
- # If the first
if [[ ! "${PIHOLE_DNS_1}" ]]; then
- # and second upstream servers do not exist
if [[ ! "${PIHOLE_DNS_2}" ]]; then
+ # If the first and second upstream servers do not exist, do not prepopulate an IP address
prePopulate=""
- # Otherwise,
else
+ # Otherwise, prepopulate the whiptail dialogue with the appropriate DNS value(s)
prePopulate=", ${PIHOLE_DNS_2}"
fi
elif [[ "${PIHOLE_DNS_1}" ]] && [[ ! "${PIHOLE_DNS_2}" ]]; then
@@ -1103,52 +927,48 @@ setDNS() {
prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}"
fi
- # Dialog for the user to enter custom upstream servers
+ # Prompt 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) || \
{ 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}")
+ # Separate the user input into the two DNS values (separated by a comma)
printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}"
printf -v PIHOLE_DNS_2 "%s" "${piholeDNS##*,}"
- # If the IP is valid,
+ # If the first DNS value is invalid or empty, this if statement will be true and we will set PIHOLE_DNS_1="Invalid"
if ! valid_ip "${PIHOLE_DNS_1}" || [[ ! "${PIHOLE_DNS_1}" ]]; then
- # store it in the variable so we can use it
PIHOLE_DNS_1=${strInvalid}
fi
- # Do the same for the secondary server
+ # If the second DNS value is invalid or empty, this if statement will be true and we will set PIHOLE_DNS_2="Invalid"
if ! valid_ip "${PIHOLE_DNS_2}" && [[ "${PIHOLE_DNS_2}" ]]; then
PIHOLE_DNS_2=${strInvalid}
fi
# If either of the DNS servers are invalid,
if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]] || [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then
- # explain this to the user
+ # explain this to the user,
whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\\n\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}
- # and set the variables back to nothing
+ # set the variables back to nothing,
if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]]; then
PIHOLE_DNS_1=""
fi
if [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then
PIHOLE_DNS_2=""
fi
- # Since the settings will not work, stay in the loop
+ # and continue the loop.
DNSSettingsCorrect=False
- # Otherwise,
else
- # Show the settings
+ # Otherwise, show the DNS setting to the user, and break the loop if they confirm them.
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,
else
- # If the settings are wrong, the loop continues
DNSSettingsCorrect=False
fi
fi
done
else
- # Save the old Internal Field Separator in a variable
+ # Save the old Internal Field Separator in a variable,
OIFS=$IFS
# and set the new one to newline
IFS=$'\n'
@@ -1187,16 +1007,16 @@ setLogging() {
# Get the user's choice
LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1)
case ${LogChoices} in
- # If it's on
+ # If it's on,
"On (Recommended)")
printf " %b Logging On.\\n" "${INFO}"
- # Set the GLOBAL variable to true so we know what they selected
+ # set the GLOBAL variable setting to true
QUERY_LOGGING=true
;;
# Otherwise, it's off,
Off)
printf " %b Logging Off.\\n" "${INFO}"
- # So set it to false
+ # set the GLOBAL variable setting to false
QUERY_LOGGING=false
;;
esac
@@ -1252,7 +1072,7 @@ setAdminFlag() {
;;
esac
- # Request user to install web server, if it has not been deselected before (INSTALL_WEB_SERVER=true is default).
+ # If the user wants to install the Web admin interface (i.e. it has not been deselected above)
if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
# Get list of required PHP modules, excluding base package (common) and handler (cgi)
local i php_modules
@@ -1291,18 +1111,18 @@ chooseBlocklists() {
# In a variable, show the choices available; exit if Cancel is selected
choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; rm "${adlistFile}" ;exit 1; }
- # For each choice available,
+ # Add all selected choices to the lists file
for choice in ${choices}
do
appendToListsFile "${choice}"
done
+ # Create an empty adList file with appropriate permissions.
touch "${adlistFile}"
chmod 644 "${adlistFile}"
}
# Accept a string parameter, it must be one of the default lists
-# This function allow to not duplicate code in chooseBlocklists and
-# in installDefaultBlocklists
+# This function saves duplication between chooseBlocklists and installDefaultBlocklists
appendToListsFile() {
case $1 in
StevenBlack ) echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}";;
@@ -1328,8 +1148,10 @@ version_check_dnsmasq() {
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"
+ local dnsmasq_pihole_01_source="${PI_HOLE_LOCAL_REPO}/advanced/01-pihole.conf"
+ local dnsmasq_pihole_01_target="/etc/dnsmasq.d/01-pihole.conf"
+ local dnsmasq_rfc6761_06_source="${PI_HOLE_LOCAL_REPO}/advanced/06-rfc6761.conf"
+ local dnsmasq_rfc6761_06_target="/etc/dnsmasq.d/06-rfc6761.conf"
# If the dnsmasq config file exists
if [[ -f "${dnsmasq_conf}" ]]; then
@@ -1339,16 +1161,15 @@ version_check_dnsmasq() {
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
+ # so backup the original file,
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
install -D -m 644 -T "${dnsmasq_original_config}" "${dnsmasq_conf}"
printf "%b %b Restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}"
- # Otherwise,
else
- # Don't to anything
+ # Otherwise, don't to anything
printf " it is not a Pi-hole file, leaving alone!\\n"
fi
else
@@ -1359,47 +1180,48 @@ version_check_dnsmasq() {
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}"
+ printf " %b Installing %s..." "${INFO}" "${dnsmasq_pihole_01_target}"
# 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
install -d -m 755 "/etc/dnsmasq.d"
fi
# Copy the new Pi-hole DNS config file into the dnsmasq.d directory
- 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}"
+ install -D -m 644 -T "${dnsmasq_pihole_01_source}" "${dnsmasq_pihole_01_target}"
+ printf "%b %b Installed %s\n" "${OVER}" "${TICK}" "${dnsmasq_pihole_01_target}"
# 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}"
+ # First, swap in the interface to listen on,
+ sed -i "s/@INT@/$PIHOLE_INTERFACE/" "${dnsmasq_pihole_01_target}"
if [[ "${PIHOLE_DNS_1}" != "" ]]; then
- # Then swap in the primary DNS server
- sed -i "s/@DNS1@/$PIHOLE_DNS_1/" "${dnsmasq_pihole_01_location}"
+ # then swap in the primary DNS server.
+ sed -i "s/@DNS1@/$PIHOLE_DNS_1/" "${dnsmasq_pihole_01_target}"
else
- #
- sed -i '/^server=@DNS1@/d' "${dnsmasq_pihole_01_location}"
+ # Otherwise, remove the line which sets DNS1.
+ sed -i '/^server=@DNS1@/d' "${dnsmasq_pihole_01_target}"
fi
+ # Ditto if DNS2 is not empty
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_target}"
else
- #
- sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_location}"
+ sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_target}"
fi
# Set the cache size
- sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" ${dnsmasq_pihole_01_location}
+ sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" "${dnsmasq_pihole_01_target}"
- #
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}"
- # Otherwise,
+ # disable it by commenting out the directive in the DNS config file
+ sed -i 's/^log-queries/#log-queries/' "${dnsmasq_pihole_01_target}"
else
- # enable it by uncommenting the directive in the DNS config file
- sed -i 's/^#log-queries/log-queries/' "${dnsmasq_pihole_01_location}"
+ # Otherwise, enable it by uncommenting the directive in the DNS config file
+ sed -i 's/^#log-queries/log-queries/' "${dnsmasq_pihole_01_target}"
fi
+
+ printf " %b Installing %s..." "${INFO}" "${dnsmasq_rfc6761_06_source}"
+ install -D -m 644 -T "${dnsmasq_rfc6761_06_source}" "${dnsmasq_rfc6761_06_target}"
+ printf "%b %b Installed %s\n" "${OVER}" "${TICK}" "${dnsmasq_rfc6761_06_target}"
}
# Clean an existing installation to prepare for upgrade/reinstall
@@ -1407,14 +1229,13 @@ clean_existing() {
# Local, named variables
# ${1} Directory to clean
local clean_directory="${1}"
- # Make ${2} the new one?
+ # Pop the first argument, and shift all addresses down by one (i.e. ${2} becomes ${1})
shift
- # ${2} Array of files to remove
+ # Then, we can access all arguments ($@) without including the directory to clean
local old_files=( "$@" )
- # For each script found in the old files array
+ # Remove each script in the old_files array
for script in "${old_files[@]}"; do
- # Remove them
rm -f "${clean_directory}/${script}.sh"
done
}
@@ -1447,9 +1268,8 @@ installScripts() {
install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
- # Otherwise,
else
- # Show an error and exit
+ # Otherwise, show an error and exit
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf "\\t\\t%bError: Local repo %s not found, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"
return 1
@@ -1492,18 +1312,18 @@ installConfigs() {
# 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
+ elif [[ -f "${lighttpdConfig}" ]]; then
# back up the original
- mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig
+ mv "${lighttpdConfig}"{,.orig}
fi
# and copy in the config file Pi-hole needs
- install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf
+ install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} "${lighttpdConfig}"
# 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 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
+ sed -i 's/^\(server\.error-handler-404\s*=\s*\).*$/\1"pihole\/custom\.php"/' "${lighttpdConfig}"
fi
# Make the directories if they do not exist and set the owners
mkdir -p /run/lighttpd
@@ -1575,9 +1395,8 @@ restart_service() {
if is_command systemctl ; then
# use that to restart the service
systemctl restart "${1}" &> /dev/null
- # Otherwise,
else
- # fall back to the service command
+ # Otherwise, fall back to the service command
service "${1}" restart &> /dev/null
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
@@ -1592,9 +1411,8 @@ enable_service() {
if is_command systemctl ; then
# use that to enable the service
systemctl enable "${1}" &> /dev/null
- # Otherwise,
else
- # use update-rc.d to accomplish this
+ # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" defaults &> /dev/null
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
@@ -1609,9 +1427,8 @@ disable_service() {
if is_command systemctl ; then
# use that to disable the service
systemctl disable "${1}" &> /dev/null
- # Otherwise,
else
- # use update-rc.d to accomplish this
+ # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" disable &> /dev/null
fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
@@ -1622,9 +1439,8 @@ check_service_active() {
if is_command systemctl ; then
# use that to check the status of the service
systemctl is-enabled "${1}" &> /dev/null
- # Otherwise,
else
- # fall back to service command
+ # Otherwise, fall back to service command
service "${1}" status &> /dev/null
fi
}
@@ -1654,9 +1470,6 @@ disable_resolved_stublistener() {
}
update_package_cache() {
- # Running apt-get update/upgrade with minimal output can cause some issues with
- # requiring user input (e.g password for phpmyadmin see #218)
-
# Update package cache on apt based OSes. Do this every time since
# it's quick and packages can be updated at any time.
@@ -1666,11 +1479,10 @@ update_package_cache() {
# Create a command from the package cache variable
if eval "${UPDATE_PKG_CACHE}" &> /dev/null; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
- # Otherwise,
else
- # show an error and exit
+ # Otherwise, show an error and exit
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
- printf " %bError: Unable to update package cache. Please try \"%s\"%b" "${COL_LIGHT_RED}" "${UPDATE_PKG_CACHE}" "${COL_NC}"
+ printf " %bError: Unable to update package cache. Please try \"%s\"%b" "${COL_LIGHT_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}"
return 1
fi
}
@@ -1697,21 +1509,7 @@ notify_package_updates_available() {
fi
}
-# What's this doing outside of a function in the middle of nowhere?
-counter=0
-
install_dependent_packages() {
- # Local, named variables should be used here, especially for an iterator
- # Add one to the counter
- counter=$((counter+1))
- # If it equals 1,
- if [[ "${counter}" == 1 ]]; then
- #
- printf " %b Installer Dependency checks...\\n" "${INFO}"
- else
- #
- printf " %b Main Dependency checks...\\n" "${INFO}"
- fi
# Install packages passed in via argument array
# No spinner - conflicts with set -e
@@ -1723,7 +1521,7 @@ install_dependent_packages() {
# NOTE: We may be able to use this installArray in the future to create a list of package that were
# installed by us, and remove only the installed packages, and not the entire list.
if is_command apt-get ; then
- # For each package,
+ # For each package, check if it's already installed (and if so, don't add it to the installArray)
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
@@ -1733,8 +1531,11 @@ install_dependent_packages() {
installArray+=("${i}")
fi
done
+ # If there's anything to install, install everything in the list.
if [[ "${#installArray[@]}" -gt 0 ]]; then
test_dpkg_lock
+ # Running apt-get install with minimal output can cause some issues with
+ # requiring user input (e.g password for phpmyadmin see #218)
printf " %b Processing %s install(s) for: %s, please wait...\\n" "${INFO}" "${PKG_MANAGER}" "${installArray[*]}"
printf '%*s\n' "$columns" '' | tr " " -;
"${PKG_INSTALL[@]}" "${installArray[@]}"
@@ -1747,6 +1548,7 @@ install_dependent_packages() {
# Install Fedora/CentOS packages
for i in "$@"; do
+ # For each package, check if it's already installed (and if so, don't add it to the installArray)
printf " %b Checking for %s..." "${INFO}" "${i}"
if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then
printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}"
@@ -1755,6 +1557,7 @@ install_dependent_packages() {
installArray+=("${i}")
fi
done
+ # If there's anything to install, install everything in the list.
if [[ "${#installArray[@]}" -gt 0 ]]; then
printf " %b Processing %s install(s) for: %s, please wait...\\n" "${INFO}" "${PKG_MANAGER}" "${installArray[*]}"
printf '%*s\n' "$columns" '' | tr " " -;
@@ -1772,7 +1575,7 @@ installPiholeWeb() {
local str="Creating directory for blocking page, and copying files"
printf " %b %s..." "${INFO}" "${str}"
- # Install the directory
+ # Install the directory,
install -d -m 0755 ${PI_HOLE_BLOCKPAGE_DIR}
# and the blockpage
install -D -m 644 ${PI_HOLE_LOCAL_REPO}/advanced/{index,blockingpage}.* ${PI_HOLE_BLOCKPAGE_DIR}/
@@ -1791,9 +1594,8 @@ installPiholeWeb() {
# back it up
mv ${webroot}/index.lighttpd.html ${webroot}/index.lighttpd.orig
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
- # Otherwise,
else
- # don't do anything
+ # Otherwise, don't do anything
printf "%b %b %s\\n" "${OVER}" "${INFO}" "${str}"
printf " No default index.lighttpd.html file found... not backing up\\n"
fi
@@ -1801,7 +1603,7 @@ installPiholeWeb() {
# Install Sudoers file
local str="Installing sudoer file"
printf "\\n %b %s..." "${INFO}" "${str}"
- # Make the .d directory if it doesn't exist
+ # Make the .d directory if it doesn't exist,
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
@@ -1845,11 +1647,11 @@ runGravity() {
create_pihole_user() {
local str="Checking for user 'pihole'"
printf " %b %s..." "${INFO}" "${str}"
- # If the user pihole exists,
+ # If the pihole user exists,
if id -u pihole &> /dev/null; then
- # if group exists
+ # and if the pihole group exists,
if getent group pihole > /dev/null 2>&1; then
- # just show a success
+ # succeed
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else
local str="Checking for group 'pihole'"
@@ -1870,14 +1672,14 @@ create_pihole_user() {
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
fi
fi
- # Otherwise,
else
+ # If the pihole user doesn't exist,
printf "%b %b %s" "${OVER}" "${CROSS}" "${str}"
local str="Creating user 'pihole'"
printf "%b %b %s..." "${OVER}" "${INFO}" "${str}"
- # create her with the useradd command
+ # create her with the useradd command,
if getent group pihole > /dev/null 2>&1; then
- # add primary group pihole as it already exists
+ # then add her to the pihole group (as it already exists)
if useradd -r --no-user-group -g pihole -s /usr/sbin/nologin pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else
@@ -1894,7 +1696,7 @@ create_pihole_user() {
fi
}
-#
+# This function saves any changes to the setup variables into the setupvars.conf file for future runs
finalExports() {
# If the Web interface is not set to be installed,
if [[ "${INSTALL_WEB_INTERFACE}" == false ]]; then
@@ -1912,7 +1714,7 @@ finalExports() {
# If the setup variable file exists,
if [[ -e "${setupVars}" ]]; then
# update the variables in the file
- sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1\b/d;/PIHOLE_DNS_2\b/d;/QUERY_LOGGING/d;/INSTALL_WEB_SERVER/d;/INSTALL_WEB_INTERFACE/d;/LIGHTTPD_ENABLED/d;/CACHE_SIZE/d;' "${setupVars}"
+ sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1\b/d;/PIHOLE_DNS_2\b/d;/QUERY_LOGGING/d;/INSTALL_WEB_SERVER/d;/INSTALL_WEB_INTERFACE/d;/LIGHTTPD_ENABLED/d;/CACHE_SIZE/d;/DNS_FQDN_REQUIRED/d;/DNS_BOGUS_PRIV/d;' "${setupVars}"
fi
# echo the information to the user
{
@@ -1926,6 +1728,8 @@ finalExports() {
echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}"
echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}"
echo "CACHE_SIZE=${CACHE_SIZE}"
+ echo "DNS_FQDN_REQUIRED=${DNS_FQDN_REQUIRED:-true}"
+ echo "DNS_BOGUS_PRIV=${DNS_BOGUS_PRIV:-true}"
}>> "${setupVars}"
chmod 644 "${setupVars}"
@@ -1946,11 +1750,18 @@ finalExports() {
# Install the logrotate script
installLogrotate() {
-
local str="Installing latest logrotate script"
+ local target=/etc/pihole/logrotate
+
printf "\\n %b %s..." "${INFO}" "${str}"
+ if [[ -f ${target} ]]; then
+ printf "\\n\\t%b Existing logrotate file found. No changes made.\\n" "${INFO}"
+ # Return value isn't that important, using 2 to indicate that it's not a fatal error but
+ # the function did not complete.
+ return 2
+ fi
# Copy the file over from the local repo
- install -D -m 644 -T ${PI_HOLE_LOCAL_REPO}/advanced/Templates/logrotate /etc/pihole/logrotate
+ install -D -m 644 -T "${PI_HOLE_LOCAL_REPO}"/advanced/Templates/logrotate ${target}
# 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.
@@ -1958,35 +1769,14 @@ installLogrotate() {
# customize the logrotate script here in order to reflect
# the local properties of the /var/log directory
logusergroup="$(stat -c '%U %G' /var/log)"
- # If the variable has a value,
- if [[ ! -z "${logusergroup}" ]]; then
- #
- sed -i "s/# su #/su ${logusergroup}/g;" /etc/pihole/logrotate
+ # If there is a usergroup for log rotation,
+ if [[ -n "${logusergroup}" ]]; then
+ # replace the line in the logrotate script with that usergroup.
+ sed -i "s/# su #/su ${logusergroup}/g;" ${target}
fi
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
}
-# 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}"
- # 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}"
- fi
-}
-
# Install base files and web interface
installPihole() {
# If the user wants to install the Web interface,
@@ -2017,10 +1807,6 @@ installPihole() {
fi
fi
fi
- # For updates and unattended install.
- if [[ "${useUpdateVars}" == true ]]; then
- accountForRefactor
- fi
# Install base files and web interface
if ! installScripts; then
printf " %b Failure in dependent script copy function.\\n" "${CROSS}"
@@ -2038,8 +1824,10 @@ installPihole() {
fi
# Install the cron file
installCron
+
# Install the logrotate file
- installLogrotate
+ installLogrotate || true
+
# Check if dnsmasq is present. If so, disable it and back up any possible
# config file
disable_dnsmasq
@@ -2099,15 +1887,15 @@ checkSelinux() {
# Installation complete message with instructions for the user
displayFinalMessage() {
- # If
+ # If the number of arguments is > 0,
if [[ "${#1}" -gt 0 ]] ; then
+ # set the password to the first argument.
pwstring="$1"
- # else, if the dashboard password in the setup variables exists,
- elif [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) -gt 0 ]]; then
- # set a variable for evaluation later
+ elif [[ $(grep 'WEBPASSWORD' -c "${setupVars}") -gt 0 ]]; then
+ # Else if the password exists from previous setup, we'll load it later
pwstring="unchanged"
else
- # set a variable for evaluation later
+ # Else, inform the user that there is no set password.
pwstring="NOT SET"
fi
# If the user wants to install the dashboard,
@@ -2124,9 +1912,7 @@ Your Admin Webpage login password is ${pwstring}"
IPv4: ${IPV4_ADDRESS%/*}
IPv6: ${IPV6_ADDRESS:-"Not Configured"}
-If you set a new IP address, you should restart the Pi.
-
-The install log is in /etc/pihole.
+If you have not done so already, the above IP should be set to static.
${additional}" "${r}" "${c}"
}
@@ -2138,15 +1924,14 @@ update_dialogs() {
opt1a="Repair"
opt1b="This will retain existing settings"
strAdd="You will remain on the same version"
- # Otherwise,
else
- # set some variables with different values
+ # Otherwise, set some variables with different values
opt1a="Update"
opt1b="This will retain existing settings."
strAdd="You will be updated to the latest version."
fi
opt2a="Reconfigure"
- opt2b="This will reset your Pi-hole and allow you to enter new settings."
+ opt2b="Resets Pi-hole and allows re-selecting 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 \
@@ -2241,7 +2026,7 @@ checkout_pull_branch() {
# 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)
+ git_pull=$(git pull --no-rebase || return 1)
if [[ "$git_pull" == *"up-to-date"* ]]; then
printf " %b %s\\n" "${INFO}" "${git_pull}"
@@ -2291,7 +2076,6 @@ clone_or_update_repos() {
# Disable directive for SC2120 a value _can_ be passed to this function, but it is passed from an external script that sources this one
# shellcheck disable=SC2120
FTLinstall() {
-
# Local, named variables
local latesttag
local str="Downloading and Installing FTL"
@@ -2322,9 +2106,8 @@ FTLinstall() {
url="https://ftl.pi-hole.net/${ftlBranch}"
fi
- # If the download worked,
if curl -sSL --fail "${url}/${binary}" -o "${binary}"; then
- # get sha1 of the binary we just downloaded for verification.
+ # If the download worked, get sha1 of the binary we just downloaded for verification.
curl -sSL --fail "${url}/${binary}.sha1" -o "${binary}.sha1"
# If we downloaded binary file (as opposed to text),
@@ -2333,8 +2116,6 @@ 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
@@ -2348,16 +2129,15 @@ FTLinstall() {
# Installed the FTL service
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
return 0
- # Otherwise,
else
- # the download failed, so just go back to the original directory
+ # Otherwise, the hash download failed, so print and exit.
popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; }
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %bError: Download of %s/%s failed (checksum error)%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}"
return 1
fi
- # Otherwise,
else
+ # Otherwise, the download failed, so print and exit.
popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; }
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
# The URL could not be found
@@ -2400,18 +2180,14 @@ get_binary_name() {
# If the machine is arm or aarch
if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then
# ARM
- #
local rev
rev=$(uname -m | sed "s/[^0-9]//g;")
- #
local lib
lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }')
- #
if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then
printf "%b %b Detected AArch64 (64 Bit ARM) processor\\n" "${OVER}" "${TICK}"
# set the binary to be used
l_binary="pihole-FTL-aarch64-linux-gnu"
- #
elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then
# Hard-float available: Use gnueabihf binaries
# If ARMv8 or higher is found (e.g., BCM2837 as found in Raspberry Pi Model 3B)
@@ -2419,13 +2195,13 @@ get_binary_name() {
printf "%b %b Detected ARMv8 (or newer) processor\\n" "${OVER}" "${TICK}"
# set the binary to be used
l_binary="pihole-FTL-armv8-linux-gnueabihf"
- # Otherwise, if ARMv7 is found (e.g., BCM2836 as found in Raspberry Pi Model 2)
elif [[ "${rev}" -eq 7 ]]; then
+ # Otherwise, if ARMv7 is found (e.g., BCM2836 as found in Raspberry Pi Model 2)
printf "%b %b Detected ARMv7 processor (with hard-float support)\\n" "${OVER}" "${TICK}"
# set the binary to be used
l_binary="pihole-FTL-armv7-linux-gnueabihf"
- # Otherwise, use the ARMv6 binary (e.g., BCM2835 as found in Raspberry Pi Zero and Model 1)
else
+ # Otherwise, use the ARMv6 binary (e.g., BCM2835 as found in Raspberry Pi Zero and Model 1)
printf "%b %b Detected ARMv6 processor (with hard-float support)\\n" "${OVER}" "${TICK}"
# set the binary to be used
l_binary="pihole-FTL-armv6-linux-gnueabihf"
@@ -2475,6 +2251,7 @@ get_binary_name() {
l_binary="pihole-FTL-linux-x86_32"
fi
+ # Returning a string value via echo
echo ${l_binary}
}
@@ -2611,9 +2388,8 @@ main() {
# Show the Pi-hole logo so people know it's genuine since the logo and name are trademarked
show_ascii_berry
make_temporary_log
- # Otherwise,
else
- # They do not have enough privileges, so let the user know
+ # Otherwise, they do not have enough privileges, so let the user know
printf " %b %s\\n" "${INFO}" "${str}"
printf " %b %bScript called with non-root privileges%b\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}"
printf " The Pi-hole requires elevated privileges to install and run\\n"
@@ -2621,7 +2397,7 @@ main() {
printf " Make sure to download this script from a trusted source\\n\\n"
printf " %b Sudo utility check" "${INFO}"
- # If the sudo command exists,
+ # If the sudo command exists, try rerunning as admin
if is_command sudo ; then
printf "%b %b Sudo utility check\\n" "${OVER}" "${TICK}"
@@ -2635,9 +2411,8 @@ main() {
fi
exit $?
- # Otherwise,
else
- # Let them know they need to run it as root
+ # Otherwise, tell the user they need to run the script as root, and bail
printf "%b %b Sudo utility check\\n" "${OVER}" "${CROSS}"
printf " %b Sudo is needed for the Web Interface to run pihole commands\\n\\n" "${INFO}"
printf " %b %bPlease re-run this installer as root${COL_NC}\\n" "${INFO}" "${COL_LIGHT_RED}"
@@ -2645,8 +2420,30 @@ main() {
fi
fi
- # Check for supported distribution
- distro_check
+ # Check for supported package managers so that we may install dependencies
+ package_manager_detect
+
+ # Notify user of package availability
+ notify_package_updates_available
+
+ # Install packages necessary to perform os_check
+ printf " %b Checking for / installing Required dependencies for OS Check...\\n" "${INFO}"
+ install_dependent_packages "${OS_CHECK_DEPS[@]}"
+
+ # Check that the installed OS is officially supported - display warning if not
+ os_check
+
+ # Install packages used by this installation script
+ printf " %b Checking for / installing Required dependencies for this install script...\\n" "${INFO}"
+ install_dependent_packages "${INSTALLER_DEPS[@]}"
+
+ #In case of RPM based distro, select the proper PHP version
+ if [[ "$PKG_MANAGER" == "yum" || "$PKG_MANAGER" == "dnf" ]] ; then
+ select_rpm_php
+ fi
+
+ # Check if SELinux is Enforcing
+ checkSelinux
# If the setup variable file exists,
if [[ -f "${setupVars}" ]]; then
@@ -2657,26 +2454,12 @@ main() {
useUpdateVars=true
# also disable debconf-apt-progress dialogs
export DEBIAN_FRONTEND="noninteractive"
- # Otherwise,
else
- # show the available options (repair/reconfigure)
+ # If running attended, show the available options (repair/reconfigure)
update_dialogs
fi
fi
- # Start the installer
- # Notify user of package availability
- notify_package_updates_available
-
- # Install packages used by this installation script
- install_dependent_packages "${INSTALLER_DEPS[@]}"
-
- # Check that the installed OS is officially supported - display warning if not
- os_check
-
- # Check if SELinux is Enforcing
- checkSelinux
-
if [[ "${useUpdateVars}" == false ]]; then
# Display welcome dialogs
welcomeDialogs
@@ -2690,8 +2473,8 @@ main() {
setDNS
# Give the user a choice of blocklists to include in their install. Or not.
chooseBlocklists
- # Let the user decide if they want to block ads over IPv4 and/or IPv6
- use4andor6
+ # find IPv4 and IPv6 information of the device
+ collect_v4andv6_information
# Let the user decide if they want the web interface to be installed automatically
setAdminFlag
# Let the user decide if they want query logging enabled...
@@ -2713,21 +2496,23 @@ main() {
PRIVACY_LEVEL="${PRIVACY_LEVEL:-0}"
fi
fi
- # Clone/Update the repos
+ # Download or update the scripts by updating the appropriate git repos
clone_or_update_repos
# Install the Core dependencies
local dep_install_list=("${PIHOLE_DEPS[@]}")
if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
- # Install the Web dependencies
+ # And, if the setting says so, install the Web admin interface dependencies
dep_install_list+=("${PIHOLE_WEB_DEPS[@]}")
fi
+ # Install packages used by the actual software
+ printf " %b Checking for / installing Required dependencies for Pi-hole software...\\n" "${INFO}"
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
- # has chosen to install the web interface, else the `LIGHTTPD_ENABLED` check will fail
+ # has chosen to install the web interface, else the LIGHTTPD_ENABLED check will fail
if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
enable_service lighttpd
fi
@@ -2762,7 +2547,7 @@ main() {
# Add password to web UI if there is none
pw=""
# If no password is set,
- if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then
+ if [[ $(grep 'WEBPASSWORD' -c "${setupVars}") == 0 ]] ; then
# generate a random password
pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
# shellcheck disable=SC1091
@@ -2779,7 +2564,6 @@ main() {
# If the Web server was installed,
if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
-
if [[ "${LIGHTTPD_ENABLED}" == true ]]; then
restart_service lighttpd
enable_service lighttpd
@@ -2828,7 +2612,7 @@ main() {
printf " %b You may now configure your devices to use the Pi-hole as their DNS server\\n" "${INFO}"
[[ -n "${IPV4_ADDRESS%/*}" ]] && printf " %b Pi-hole DNS (IPv4): %s\\n" "${INFO}" "${IPV4_ADDRESS%/*}"
[[ -n "${IPV6_ADDRESS}" ]] && printf " %b Pi-hole DNS (IPv6): %s\\n" "${INFO}" "${IPV6_ADDRESS}"
- printf " %b If you set a new IP address, please restart the server running the Pi-hole\\n" "${INFO}"
+ printf " %b If you have not done so already, the above IP should be set to static.\\n" "${INFO}"
INSTALL_TYPE="Installation"
else
INSTALL_TYPE="Update"
diff --git a/automated install/uninstall.sh b/automated install/uninstall.sh
index 6e36d16b..5e27514f 100755
--- a/automated install/uninstall.sh
+++ b/automated install/uninstall.sh
@@ -31,7 +31,7 @@ else
else
echo -e " ${CROSS} ${str}
Script called with non-root privileges
- The Pi-hole requires elevated privleges to uninstall"
+ The Pi-hole requires elevated privileges to uninstall"
exit 1
fi
fi
@@ -42,8 +42,8 @@ source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# setupVars set in basic-install.sh
source "${setupVars}"
-# distro_check() sourced from basic-install.sh
-distro_check
+# package_manager_detect() sourced from basic-install.sh
+package_manager_detect
# Install packages used by the Pi-hole
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}")
@@ -113,7 +113,7 @@ removeNoPurge() {
fi
fi
echo -e "${OVER} ${TICK} Removed Web Interface"
-
+
# Attempt to preserve backwards compatibility with older versions
# to guarantee no additional changes were made to /etc/crontab after
# the installation of pihole, /etc/crontab.pihole should be permanently
@@ -145,6 +145,7 @@ removeNoPurge() {
${SUDO} rm -f /etc/dnsmasq.d/adList.conf &> /dev/null
${SUDO} rm -f /etc/dnsmasq.d/01-pihole.conf &> /dev/null
+ ${SUDO} rm -f /etc/dnsmasq.d/06-rfc6761.conf &> /dev/null
${SUDO} rm -rf /var/log/*pihole* &> /dev/null
${SUDO} rm -rf /etc/pihole/ &> /dev/null
${SUDO} rm -rf /etc/.pihole/ &> /dev/null
@@ -206,11 +207,7 @@ removeNoPurge() {
}
######### SCRIPT ###########
-if command -v vcgencmd &> /dev/null; then
- echo -e " ${INFO} All dependencies are safe to remove on Raspbian"
-else
- echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
-fi
+echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
while true; do
echo -e " ${INFO} ${COL_YELLOW}The following dependencies may have been added by the Pi-hole install:"
echo -n " "
diff --git a/gravity.sh b/gravity.sh
index dfb863e9..dfaf4fea 100755
--- a/gravity.sh
+++ b/gravity.sh
@@ -15,8 +15,6 @@ export LC_ALL=C
coltable="/opt/pihole/COL_TABLE"
source "${coltable}"
-regexconverter="/opt/pihole/wildcard_regex_converter.sh"
-source "${regexconverter}"
# shellcheck disable=SC1091
source "/etc/.pihole/advanced/Scripts/database_migration/gravity-db.sh"
@@ -35,8 +33,9 @@ localList="${piholeDir}/local.list"
VPNList="/etc/openvpn/ipp.txt"
piholeGitDir="/etc/.pihole"
-gravityDBfile="${piholeDir}/gravity.db"
-gravityTEMPfile="${piholeDir}/gravity_temp.db"
+gravityDBfile_default="${piholeDir}/gravity.db"
+# GRAVITYDB may be overwritten by source pihole-FTL.conf below
+GRAVITYDB="${gravityDBfile_default}"
gravityDBschema="${piholeGitDir}/advanced/Templates/gravity.db.sql"
gravityDBcopy="${piholeGitDir}/advanced/Templates/gravity_copy.sql"
@@ -46,16 +45,6 @@ domainsExtension="domains"
setupVars="${piholeDir}/setupVars.conf"
if [[ -f "${setupVars}" ]];then
source "${setupVars}"
-
- # Remove CIDR mask from IPv4/6 addresses
- IPV4_ADDRESS="${IPV4_ADDRESS%/*}"
- IPV6_ADDRESS="${IPV6_ADDRESS%/*}"
-
- # Determine if IPv4/6 addresses exist
- if [[ -z "${IPV4_ADDRESS}" ]] && [[ -z "${IPV6_ADDRESS}" ]]; then
- echo -e " ${COL_LIGHT_RED}No IP addresses found! Please run 'pihole -r' to reconfigure${COL_NC}"
- exit 1
- fi
else
echo -e " ${COL_LIGHT_RED}Installation Failure: ${setupVars} does not exist! ${COL_NC}
Please run 'pihole -r', and choose the 'reconfigure' option to fix."
@@ -68,6 +57,13 @@ if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}"
fi
+# Set this only after sourcing pihole-FTL.conf as the gravity database path may
+# have changed
+gravityDBfile="${GRAVITYDB}"
+gravityTEMPfile="${GRAVITYDB}_temp"
+gravityDIR="$(dirname -- "${gravityDBfile}")"
+gravityOLDfile="${gravityDIR}/gravity_old.db"
+
if [[ -z "${BLOCKINGMODE}" ]] ; then
BLOCKINGMODE="NULL"
fi
@@ -84,11 +80,11 @@ generate_gravity_database() {
# Copy data from old to new database file and swap them
gravity_swap_databases() {
- local str
+ local str copyGravity
str="Building tree"
echo -ne " ${INFO} ${str}..."
- # The index is intentionally not UNIQUE as prro quality adlists may contain domains more than once
+ # The index is intentionally not UNIQUE as poor quality adlists may contain domains more than once
output=$( { sqlite3 "${gravityTEMPfile}" "CREATE INDEX idx_gravity ON gravity (domain, adlist_id);"; } 2>&1 )
status="$?"
@@ -101,7 +97,14 @@ gravity_swap_databases() {
str="Swapping databases"
echo -ne " ${INFO} ${str}..."
- output=$( { sqlite3 "${gravityTEMPfile}" < "${gravityDBcopy}"; } 2>&1 )
+ # Gravity copying SQL script
+ copyGravity="$(cat "${gravityDBcopy}")"
+ if [[ "${gravityDBfile}" != "${gravityDBfile_default}" ]]; then
+ # Replace default gravity script location by custom location
+ copyGravity="${copyGravity//"${gravityDBfile_default}"/"${gravityDBfile}"}"
+ fi
+
+ output=$( { sqlite3 "${gravityTEMPfile}" <<< "${copyGravity}"; } 2>&1 )
status="$?"
if [[ "${status}" -ne 0 ]]; then
@@ -110,8 +113,19 @@ gravity_swap_databases() {
fi
echo -e "${OVER} ${TICK} ${str}"
- # Swap databases and remove old database
- rm "${gravityDBfile}"
+ # Swap databases and remove or conditionally rename old database
+ # Number of available blocks on disk
+ availableBlocks=$(stat -f --format "%a" "${gravityDIR}")
+ # Number of blocks, used by gravity.db
+ gravityBlocks=$(stat --format "%b" ${gravityDBfile})
+ # Only keep the old database if available disk space is at least twice the size of the existing gravity.db.
+ # Better be safe than sorry...
+ if [ "${availableBlocks}" -gt "$((gravityBlocks * 2))" ] && [ -f "${gravityDBfile}" ]; then
+ echo -e " ${TICK} The old database remains available."
+ mv "${gravityDBfile}" "${gravityOLDfile}"
+ else
+ rm "${gravityDBfile}"
+ fi
mv "${gravityTEMPfile}" "${gravityDBfile}"
}
@@ -176,7 +190,7 @@ database_table_from_file() {
echo "${rowid},\"${domain}\",${timestamp}" >> "${tmpFile}"
elif [[ "${table}" == "adlist" ]]; then
# Adlist table format
- echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\"," >> "${tmpFile}"
+ echo "${rowid},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\",,0,0,0" >> "${tmpFile}"
else
# White-, black-, and regexlist table format
echo "${rowid},${type},\"${domain}\",1,${timestamp},${timestamp},\"Migrated from ${source}\"" >> "${tmpFile}"
@@ -217,6 +231,48 @@ database_adlist_updated() {
fi
}
+# Check if a column with name ${2} exists in gravity table with name ${1}
+gravity_column_exists() {
+ output=$( { printf ".timeout 30000\\nSELECT EXISTS(SELECT * FROM pragma_table_info('%s') WHERE name='%s');\\n" "${1}" "${2}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
+ if [[ "${output}" == "1" ]]; then
+ return 0 # Bash 0 is success
+ fi
+
+ return 1 # Bash non-0 is failure
+}
+
+# Update number of domain on this list. We store this in the "old" database as all values in the new database will later be overwritten
+database_adlist_number() {
+ # Only try to set number of domains when this field exists in the gravity database
+ if ! gravity_column_exists "adlist" "number"; then
+ return;
+ fi
+
+ output=$( { printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${num_lines}" "${num_invalid}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
+ status="$?"
+
+ if [[ "${status}" -ne 0 ]]; then
+ echo -e "\\n ${CROSS} Unable to update number of domains in adlist with ID ${1} in database ${gravityDBfile}\\n ${output}"
+ gravity_Cleanup "error"
+ fi
+}
+
+# Update status of this list. We store this in the "old" database as all values in the new database will later be overwritten
+database_adlist_status() {
+ # Only try to set the status when this field exists in the gravity database
+ if ! gravity_column_exists "adlist" "status"; then
+ return;
+ fi
+
+ output=$( { printf ".timeout 30000\\nUPDATE adlist SET status = %i WHERE id = %i;\\n" "${2}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
+ status="$?"
+
+ if [[ "${status}" -ne 0 ]]; then
+ echo -e "\\n ${CROSS} Unable to update status of adlist with ID ${1} in database ${gravityDBfile}\\n ${output}"
+ gravity_Cleanup "error"
+ fi
+}
+
# Migrate pre-v5.0 list files to database-based Pi-hole versions
migrate_to_database() {
# Create database file only if not present
@@ -317,6 +373,10 @@ gravity_CheckDNSResolutionAvailable() {
gravity_DownloadBlocklists() {
echo -e " ${INFO} ${COL_BOLD}Neutrino emissions detected${COL_NC}..."
+ if [[ "${gravityDBfile}" != "${gravityDBfile_default}" ]]; then
+ echo -e " ${INFO} Storing gravity database in ${COL_BOLD}${gravityDBfile}${COL_NC}"
+ fi
+
# Retrieve source URLs from gravity database
# We source only enabled adlists, sqlite3 stores boolean values as 0 (false) or 1 (true)
mapfile -t sources <<< "$(sqlite3 "${gravityDBfile}" "SELECT address FROM vw_adlist;" 2> /dev/null)"
@@ -363,7 +423,7 @@ gravity_DownloadBlocklists() {
target="$(mktemp -p "/tmp" --suffix=".gravity")"
- # Use compression to reduce the amount of data that is transfered
+ # Use compression to reduce the amount of data that is transferred
# between the Pi-hole and the ad list provider. Use this feature
# only if it is supported by the locally available version of curl
if curl -V | grep -q "Features:.* libz"; then
@@ -444,6 +504,8 @@ gravity_DownloadBlocklists() {
}
total_num=0
+num_lines=0
+num_invalid=0
parseList() {
local adlistID="${1}" src="${2}" target="${3}" incorrect_lines
# This sed does the following things:
@@ -454,7 +516,7 @@ parseList() {
# Find (up to) five domains containing invalid characters (see above)
incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)"
- local num_lines num_target_lines num_correct_lines num_invalid
+ local num_target_lines num_correct_lines num_invalid
# Get number of lines in source file
num_lines="$(grep -c "^" "${src}")"
# Get number of lines in destination file
@@ -463,9 +525,9 @@ parseList() {
total_num="$num_target_lines"
num_invalid="$(( num_lines-num_correct_lines ))"
if [[ "${num_invalid}" -eq 0 ]]; then
- echo " ${INFO} Received ${num_lines} domains"
+ echo " ${INFO} Analyzed ${num_lines} domains"
else
- echo " ${INFO} Received ${num_lines} domains, ${num_invalid} domains invalid!"
+ echo " ${INFO} Analyzed ${num_lines} domains, ${num_invalid} domains invalid!"
fi
# Display sample of invalid lines if we found some
@@ -476,11 +538,34 @@ parseList() {
done <<< "${incorrect_lines}"
fi
}
+compareLists() {
+ local adlistID="${1}" target="${2}"
+
+ # Verify checksum when an older checksum exists
+ if [[ -s "${target}.sha1" ]]; then
+ if ! sha1sum --check --status --strict "${target}.sha1"; then
+ # The list changed upstream, we need to update the checksum
+ sha1sum "${target}" > "${target}.sha1"
+ echo " ${INFO} List has been updated"
+ database_adlist_status "${adlistID}" "1"
+ database_adlist_updated "${adlistID}"
+ else
+ echo " ${INFO} List stayed unchanged"
+ database_adlist_status "${adlistID}" "2"
+ fi
+ else
+ # No checksum available, create one for comparing on the next run
+ sha1sum "${target}" > "${target}.sha1"
+ # We assume here it was changed upstream
+ database_adlist_status "${adlistID}" "1"
+ database_adlist_updated "${adlistID}"
+ fi
+}
# Download specified URL and perform checks on HTTP status and file content
gravity_DownloadBlocklistFromUrl() {
local url="${1}" cmd_ext="${2}" agent="${3}" adlistID="${4}" saveLocation="${5}" target="${6}" compression="${7}"
- local heisenbergCompensator="" patternBuffer str httpCode success=""
+ local heisenbergCompensator="" patternBuffer str httpCode success="" ip
# Create temp file to store content on disk instead of RAM
patternBuffer=$(mktemp -p "/tmp" --suffix=".phgpb")
@@ -498,7 +583,10 @@ gravity_DownloadBlocklistFromUrl() {
blocked=false
case $BLOCKINGMODE in
"IP-NODATA-AAAA"|"IP")
- if [[ $(dig "${domain}" +short | grep "${IPV4_ADDRESS}" -c) -ge 1 ]]; then
+ # Get IP address of this domain
+ ip="$(dig "${domain}" +short)"
+ # Check if this IP matches any IP of the system
+ if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then
blocked=true
fi;;
"NXDOMAIN")
@@ -559,31 +647,49 @@ gravity_DownloadBlocklistFromUrl() {
esac;;
esac
+ local done="false"
# Determine if the blocklist was downloaded and saved correctly
if [[ "${success}" == true ]]; then
if [[ "${httpCode}" == "304" ]]; then
# Add domains to database table file
parseList "${adlistID}" "${saveLocation}" "${target}"
+ database_adlist_status "${adlistID}" "2"
+ database_adlist_number "${adlistID}"
+ done="true"
# Check if $patternbuffer is a non-zero length file
elif [[ -s "${patternBuffer}" ]]; then
# Determine if blocklist is non-standard and parse as appropriate
gravity_ParseFileIntoDomains "${patternBuffer}" "${saveLocation}"
# Add domains to database table file
parseList "${adlistID}" "${saveLocation}" "${target}"
- # Update date_updated field in gravity database table
- database_adlist_updated "${adlistID}"
+ # Compare lists, are they identical?
+ compareLists "${adlistID}" "${saveLocation}"
+ # Update gravity database table (status and updated timestamp are set in
+ # compareLists)
+ database_adlist_number "${adlistID}"
+ done="true"
else
# Fall back to previously cached list if $patternBuffer is empty
- echo -e " ${INFO} Received empty file: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}"
+ echo -e " ${INFO} Received empty file"
fi
- else
+ fi
+
+ # Do we need to fall back to a cached list (if available)?
+ if [[ "${done}" != "true" ]]; then
# Determine if cached list has read permission
if [[ -r "${saveLocation}" ]]; then
echo -e " ${CROSS} List download failed: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}"
# Add domains to database table file
parseList "${adlistID}" "${saveLocation}" "${target}"
+ database_adlist_number "${adlistID}"
+ database_adlist_status "${adlistID}" "3"
else
echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}"
+ # Manually reset these two numbers because we do not call parseList here
+ num_lines=0
+ num_invalid=0
+ database_adlist_number "${adlistID}"
+ database_adlist_status "${adlistID}" "4"
fi
fi
}
@@ -595,7 +701,7 @@ gravity_ParseFileIntoDomains() {
# Determine if we are parsing a consolidated list
#if [[ "${source}" == "${piholeDir}/${matterAndLight}" ]]; then
# Remove comments and print only the domain name
- # Most of the lists downloaded are already in hosts file format but the spacing/formating is not contiguous
+ # Most of the lists downloaded are already in hosts file format but the spacing/formatting is not contiguous
# 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
# 1) Remove carriage returns
@@ -683,43 +789,12 @@ gravity_ShowCount() {
gravity_Table_Count "vw_regex_whitelist" "regex whitelist filters"
}
-# Parse list of domains into hosts format
-gravity_ParseDomainsIntoHosts() {
- awk -v ipv4="$IPV4_ADDRESS" -v ipv6="$IPV6_ADDRESS" '{
- # Remove windows CR line endings
- sub(/\r$/, "")
- # Parse each line as "ipaddr domain"
- if(ipv6 && ipv4) {
- print ipv4" "$0"\n"ipv6" "$0
- } else if(!ipv6) {
- print ipv4" "$0
- } else {
- print ipv6" "$0
- }
- }' >> "${2}" < "${1}"
-}
-
# Create "localhost" entries into hosts format
gravity_generateLocalList() {
- local hostname
-
- if [[ -s "/etc/hostname" ]]; then
- hostname=$(< "/etc/hostname")
- elif command -v hostname &> /dev/null; then
- hostname=$(hostname -f)
- else
- echo -e " ${CROSS} Unable to determine fully qualified domain name of host"
- return 0
- fi
-
- echo -e "${hostname}\\npi.hole" > "${localList}.tmp"
-
# Empty $localList if it already exists, otherwise, create it
- : > "${localList}"
+ echo "### Do not modify this file, it will be overwritten by pihole -g" > "${localList}"
chmod 644 "${localList}"
- gravity_ParseDomainsIntoHosts "${localList}.tmp" "${localList}"
-
# Add additional LAN hosts provided by OpenVPN (if available)
if [[ -f "${VPNList}" ]]; then
awk -F, '{printf $2"\t"$1".vpn\n"}' "${VPNList}" >> "${localList}"
@@ -788,6 +863,11 @@ for var in "$@"; do
esac
done
+# Remove OLD (backup) gravity file, if it exists
+if [[ -f "${gravityOLDfile}" ]]; then
+ rm "${gravityOLDfile}"
+fi
+
# Trap Ctrl-C
gravity_Trap
diff --git a/manpages/pihole.8 b/manpages/pihole.8
index a57eb05e..aaaa8d7e 100644
--- a/manpages/pihole.8
+++ b/manpages/pihole.8
@@ -56,7 +56,7 @@ Available commands and options:
\fB-w, whitelist\fR [options] []
.br
- Adds or removes specified domain or domains tho the Whitelist
+ Adds or removes specified domain or domains to the Whitelist
.br
\fB-b, blacklist\fR [options] []
@@ -153,7 +153,7 @@ Available commands and options:
.br
-r, --refresh Set update frequency (in seconds)
.br
- -e, --exit Output stats and exit witout refreshing
+ -e, --exit Output stats and exit without refreshing
.br
\fB-g, updateGravity\fR
diff --git a/pihole b/pihole
index 2b2b7bc2..31356671 100755
--- a/pihole
+++ b/pihole
@@ -16,6 +16,7 @@ readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
# error due to modifying a readonly variable.
setupVars="/etc/pihole/setupVars.conf"
PI_HOLE_BIN_DIR="/usr/local/bin"
+readonly FTL_PID_FILE="/run/pihole-FTL.pid"
readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
source "${colfile}"
@@ -98,8 +99,25 @@ versionFunc() {
exit 0
}
+# Get PID of main pihole-FTL process
+getFTLPID() {
+ local pid
+
+ if [ -s "${FTL_PID_FILE}" ]; then
+ # -s: FILE exists and has a size greater than zero
+ pid="$(<"$FTL_PID_FILE")"
+ # Exploit prevention: unset the variable if there is malicious content
+ # Verify that the value read from the file is numeric
+ [[ "$pid" =~ [^[:digit:]] ]] && unset pid
+ fi
+
+ # If FTL is not running, or the PID file contains malicious stuff, substitute
+ # negative PID to signal this to the caller
+ echo "${pid:=-1}"
+}
+
restartDNS() {
- local svcOption svc str output status
+ local svcOption svc str output status pid icon
svcOption="${1:-restart}"
# Determine if we should reload or restart
@@ -108,17 +126,34 @@ restartDNS() {
# Note 1: This will NOT re-read any *.conf files
# Note 2: We cannot use killall here as it does
# not know about real-time signals
- svc="pkill -RTMIN pihole-FTL"
- str="Reloading DNS lists"
+ pid="$(getFTLPID)"
+ if [[ "$pid" -eq "-1" ]]; then
+ svc="true"
+ str="FTL is not running"
+ icon="${INFO}"
+ else
+ svc="kill -RTMIN ${pid}"
+ str="Reloading DNS lists"
+ icon="${TICK}"
+ fi
elif [[ "${svcOption}" =~ "reload" ]]; then
# Reloading of the DNS cache has been requested
# Note: This will NOT re-read any *.conf files
- svc="pkill -HUP pihole-FTL"
- str="Flushing DNS cache"
+ pid="$(getFTLPID)"
+ if [[ "$pid" -eq "-1" ]]; then
+ svc="true"
+ str="FTL is not running"
+ icon="${INFO}"
+ else
+ svc="kill -HUP ${pid}"
+ str="Flushing DNS cache"
+ icon="${TICK}"
+ fi
else
# A full restart has been requested
svc="service pihole-FTL restart"
str="Restarting DNS server"
+ icon="${TICK}"
fi
# Print output to Terminal, but not to Web Admin
@@ -128,7 +163,7 @@ restartDNS() {
status="$?"
if [[ "${status}" -eq 0 ]]; then
- [[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}"
+ [[ -t 1 ]] && echo -e "${OVER} ${icon} ${str}"
return 0
else
[[ ! -t 1 ]] && local OVER=""
@@ -207,7 +242,7 @@ Time:
echo "BLOCKING_ENABLED=true" >> "${setupVars}"
fi
- restartDNS reload
+ restartDNS reload-lists
echo -e "${OVER} ${TICK} ${str}"
}
@@ -328,16 +363,13 @@ tailFunc() {
fi
echo -e " ${INFO} Press Ctrl-C to exit"
- # Retrieve IPv4/6 addresses
- source /etc/pihole/setupVars.conf
-
# Strip date from each line
# Color blocklist/blacklist/wildcard entries as red
# Color A/AAAA/DHCP strings as white
# Color everything else as gray
- tail -f /var/log/pihole.log | sed -E \
+ tail -f /var/log/pihole.log | grep --line-buffered "${1}" | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \
- -e "s,(.*(blacklisted |gravity blocked ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \
+ -e "s,(.*(blacklisted |gravity blocked ).*),${COL_RED}&${COL_NC}," \
-e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
-e "s,.*,${COL_GRAY}&${COL_NC},"
exit 0
@@ -367,34 +399,24 @@ Branches:
}
tricorderFunc() {
+ local tricorder_token
if [[ ! -p "/dev/stdin" ]]; then
echo -e " ${INFO} Please do not call Tricorder directly"
exit 1
fi
- if ! (echo > /dev/tcp/tricorder.pi-hole.net/9998) >/dev/null 2>&1; then
- echo -e " ${CROSS} Unable to connect to Pi-hole's Tricorder server"
- exit 1
- fi
-
- if command -v openssl &> /dev/null; then
- openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin
- exit "$?"
- else
- echo -e " ${INFO} ${COL_YELLOW}Security Notice${COL_NC}: ${COL_WHITE}openssl${COL_NC} is not installed
- Your debug log will be transmitted unencrypted via plain-text
- There is a possibility that this could be intercepted by a third party
- If you wish to cancel, press Ctrl-C to exit within 10 seconds"
- secs="10"
- while [[ "$secs" -gt "0" ]]; do
- echo -ne "."
- sleep 1
- : $((secs--))
- done
- echo " "
- nc tricorder.pi-hole.net 9999 < /dev/stdin
- exit "$?"
+ tricorder_token=$(curl --silent --fail --show-error --upload-file "-" https://tricorder.pi-hole.net/upload < /dev/stdin 2>&1)
+ if [[ "${tricorder_token}" != "https://tricorder.pi-hole.net/"* ]]; then
+ echo -e "${CROSS} uploading failed, contact Pi-hole support for assistance."
+ # Log curl error (if available)
+ if [ -n "${tricorder_token}" ]; then
+ echo -e "${INFO} Error message: ${COL_RED}${tricorder_token}${COL_NC}\\n"
+ tricorder_token=""
+ fi
+ exit 1
fi
+ echo "Upload successful, your token is: ${COL_GREEN}${tricorder_token}${COL_NC}"
+ exit 0
}
updateCheckFunc() {
@@ -421,7 +443,10 @@ Debugging Options:
Add '-a' to automatically upload the log to tricorder.pi-hole.net
-f, flush Flush the Pi-hole log
-r, reconfigure Reconfigure or Repair Pi-hole subsystems
- -t, tail View the live output of the Pi-hole log
+ -t, tail [arg] View the live output of the Pi-hole log.
+ Add an optional argument to filter the log
+ (regular expressions are supported)
+
Options:
-a, admin Web interface options
@@ -495,7 +520,7 @@ case "${1}" in
"status" ) statusFunc "$2";;
"restartdns" ) restartDNS "$2";;
"-a" | "admin" ) webpageFunc "$@";;
- "-t" | "tail" ) tailFunc;;
+ "-t" | "tail" ) tailFunc "$2";;
"checkout" ) piholeCheckoutFunc "$@";;
"tricorder" ) tricorderFunc;;
"updatechecker" ) updateCheckFunc "$@";;
diff --git a/supportedos.txt b/supportedos.txt
deleted file mode 100644
index 1eb1fde6..00000000
--- a/supportedos.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-Raspbian=9,10
-Ubuntu=16,18,20
-Debian=9,10
-Fedora=31,32
-CentOS=7,8
\ No newline at end of file
diff --git a/test/_centos_7.Dockerfile b/test/_centos_7.Dockerfile
index 00543b67..434242bf 100644
--- a/test/_centos_7.Dockerfile
+++ b/test/_centos_7.Dockerfile
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_centos_8.Dockerfile b/test/_centos_8.Dockerfile
index 7444551b..afd2dc8a 100644
--- a/test/_centos_8.Dockerfile
+++ b/test/_centos_8.Dockerfile
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_debian_10.Dockerfile b/test/_debian_10.Dockerfile
index 9b72fc6a..54800d3c 100644
--- a/test/_debian_10.Dockerfile
+++ b/test/_debian_10.Dockerfile
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_debian_11.Dockerfile b/test/_debian_11.Dockerfile
new file mode 100644
index 00000000..39be027e
--- /dev/null
+++ b/test/_debian_11.Dockerfile
@@ -0,0 +1,17 @@
+FROM buildpack-deps:bullseye-scm
+
+ENV GITDIR /etc/.pihole
+ENV SCRIPTDIR /opt/pihole
+
+RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
+ADD . $GITDIR
+RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
+ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
+
+RUN true && \
+ chmod +x $SCRIPTDIR/*
+
+ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
+
+#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_debian_9.Dockerfile b/test/_debian_9.Dockerfile
index d6609ba3..c590a657 100644
--- a/test/_debian_9.Dockerfile
+++ b/test/_debian_9.Dockerfile
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_fedora_31.Dockerfile b/test/_fedora_33.Dockerfile
similarity index 87%
rename from test/_fedora_31.Dockerfile
rename to test/_fedora_33.Dockerfile
index 02dcb733..9ae94c70 100644
--- a/test/_fedora_31.Dockerfile
+++ b/test/_fedora_33.Dockerfile
@@ -1,4 +1,4 @@
-FROM fedora:31
+FROM fedora:33
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_fedora_32.Dockerfile b/test/_fedora_34.Dockerfile
similarity index 87%
rename from test/_fedora_32.Dockerfile
rename to test/_fedora_34.Dockerfile
index 869efb2b..96de18da 100644
--- a/test/_fedora_32.Dockerfile
+++ b/test/_fedora_34.Dockerfile
@@ -1,4 +1,4 @@
-FROM fedora:32
+FROM fedora:34
ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_ubuntu_16.Dockerfile b/test/_ubuntu_16.Dockerfile
index a92bc6f6..e572efd1 100644
--- a/test/_ubuntu_16.Dockerfile
+++ b/test/_ubuntu_16.Dockerfile
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
\ No newline at end of file
diff --git a/test/_ubuntu_18.Dockerfile b/test/_ubuntu_18.Dockerfile
index 2f63ea89..592c5c3f 100644
--- a/test/_ubuntu_18.Dockerfile
+++ b/test/_ubuntu_18.Dockerfile
@@ -12,5 +12,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_ubuntu_20.Dockerfile b/test/_ubuntu_20.Dockerfile
index caa6261f..80e2e007 100644
--- a/test/_ubuntu_20.Dockerfile
+++ b/test/_ubuntu_20.Dockerfile
@@ -13,5 +13,6 @@ RUN true && \
chmod +x $SCRIPTDIR/*
ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/_ubuntu_21.Dockerfile b/test/_ubuntu_21.Dockerfile
new file mode 100644
index 00000000..afddbfa9
--- /dev/null
+++ b/test/_ubuntu_21.Dockerfile
@@ -0,0 +1,18 @@
+FROM buildpack-deps:hirsute-scm
+
+ENV GITDIR /etc/.pihole
+ENV SCRIPTDIR /opt/pihole
+
+RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
+ADD . $GITDIR
+RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $SCRIPTDIR/
+ENV PATH /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
+ENV DEBIAN_FRONTEND=noninteractive
+
+RUN true && \
+ chmod +x $SCRIPTDIR/*
+
+ENV PH_TEST true
+ENV OS_CHECK_DOMAIN_NAME dev-supportedos.pi-hole.net
+
+#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \
diff --git a/test/conftest.py b/test/conftest.py
index 07166ec5..13731eb8 100644
--- a/test/conftest.py
+++ b/test/conftest.py
@@ -8,8 +8,6 @@ check_output = testinfra.get_backend(
SETUPVARS = {
'PIHOLE_INTERFACE': 'eth99',
- 'IPV4_ADDRESS': '1.1.1.1',
- 'IPV6_ADDRESS': 'FE80::240:D0FF:FE48:4672',
'PIHOLE_DNS_1': '4.2.2.1',
'PIHOLE_DNS_2': '4.2.2.2'
}
diff --git a/test/test_automated_install.py b/test/test_automated_install.py
index f6b5a87e..37ebdad2 100644
--- a/test/test_automated_install.py
+++ b/test/test_automated_install.py
@@ -11,20 +11,20 @@ from .conftest import (
)
-def test_supported_operating_system(Pihole):
+def test_supported_package_manager(Pihole):
'''
- confirm installer exists on unsupported distribution
+ confirm installer exits when no supported package manager found
'''
- # break supported package managers to emulate an unsupported distribution
+ # break supported package managers
Pihole.run('rm -rf /usr/bin/apt-get')
Pihole.run('rm -rf /usr/bin/rpm')
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
''')
- expected_stdout = cross_box + ' OS distribution not supported'
- assert expected_stdout in distro_check.stdout
- # assert distro_check.rc == 1
+ expected_stdout = cross_box + ' No supported package manager found'
+ assert expected_stdout in package_manager_detect.stdout
+ # assert package_manager_detect.rc == 1
def test_setupVars_are_sourced_to_global_scope(Pihole):
@@ -45,8 +45,6 @@ def test_setupVars_are_sourced_to_global_scope(Pihole):
# Currently debug test function only
echo "Outputting sourced variables"
echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}"
- echo "IPV4_ADDRESS=${IPV4_ADDRESS}"
- echo "IPV6_ADDRESS=${IPV6_ADDRESS}"
echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}"
echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}"
}
@@ -137,7 +135,7 @@ def test_update_package_cache_success_no_errors(Pihole):
'''
updateCache = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
update_package_cache
''')
expected_stdout = tick_box + ' Update local cache of available packages'
@@ -152,7 +150,7 @@ def test_update_package_cache_failure_no_errors(Pihole):
mock_command('apt-get', {'update': ('', '1')}, Pihole)
updateCache = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
update_package_cache
''')
expected_stdout = cross_box + ' Update local cache of available packages'
@@ -359,7 +357,7 @@ def test_FTL_download_aarch64_no_errors(Pihole):
mock_command('whiptail', {'*': ('', '0')}, Pihole)
Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
install_dependent_packages ${INSTALLER_DEPS[@]}
''')
download_binary = Pihole.run('''
@@ -423,10 +421,9 @@ def test_IPv6_only_link_local(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
- useIPv6dialog
+ find_IPv6_information
''')
- expected_stdout = ('Unable to find IPv6 ULA/GUA address, '
- 'IPv6 adblocking will not be enabled')
+ expected_stdout = ('Unable to find IPv6 ULA/GUA address')
assert expected_stdout in detectPlatform.stdout
@@ -447,9 +444,9 @@ def test_IPv6_only_ULA(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
- useIPv6dialog
+ find_IPv6_information
''')
- expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
+ expected_stdout = 'Found IPv6 ULA address'
assert expected_stdout in detectPlatform.stdout
@@ -470,9 +467,9 @@ def test_IPv6_only_GUA(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
- useIPv6dialog
+ find_IPv6_information
''')
- expected_stdout = 'Found IPv6 GUA address, using it for blocking IPv6 ads'
+ expected_stdout = 'Found IPv6 GUA address'
assert expected_stdout in detectPlatform.stdout
@@ -494,9 +491,9 @@ def test_IPv6_GUA_ULA_test(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
- useIPv6dialog
+ find_IPv6_information
''')
- expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
+ expected_stdout = 'Found IPv6 ULA address'
assert expected_stdout in detectPlatform.stdout
@@ -518,56 +515,59 @@ def test_IPv6_ULA_GUA_test(Pihole):
)
detectPlatform = Pihole.run('''
source /opt/pihole/basic-install.sh
- useIPv6dialog
+ find_IPv6_information
''')
- expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads'
+ expected_stdout = 'Found IPv6 ULA address'
assert expected_stdout in detectPlatform.stdout
-def test_validate_ip_valid(Pihole):
+def test_validate_ip(Pihole):
'''
- Given a valid IP address, valid_ip returns success
+ Tests valid_ip for various IP addresses
'''
- output = Pihole.run('''
- source /opt/pihole/basic-install.sh
- valid_ip "192.168.1.1"
- ''')
+ def test_address(addr, success=True):
+ output = Pihole.run('''
+ source /opt/pihole/basic-install.sh
+ valid_ip "{addr}"
+ '''.format(addr=addr))
- assert output.rc == 0
+ assert output.rc == 0 if success else 1
-
-def test_validate_ip_invalid_octet(Pihole):
- '''
- Given an invalid IP address (large octet), valid_ip returns an error
- '''
-
- output = Pihole.run('''
- source /opt/pihole/basic-install.sh
- valid_ip "1092.168.1.1"
- ''')
-
- assert output.rc == 1
-
-
-def test_validate_ip_invalid_letters(Pihole):
- '''
- Given an invalid IP address (contains letters), valid_ip returns an error
- '''
-
- output = Pihole.run('''
- source /opt/pihole/basic-install.sh
- valid_ip "not an IP"
- ''')
-
- assert output.rc == 1
+ test_address('192.168.1.1')
+ test_address('127.0.0.1')
+ test_address('255.255.255.255')
+ test_address('255.255.255.256', False)
+ test_address('255.255.256.255', False)
+ test_address('255.256.255.255', False)
+ test_address('256.255.255.255', False)
+ test_address('1092.168.1.1', False)
+ test_address('not an IP', False)
+ test_address('8.8.8.8#', False)
+ test_address('8.8.8.8#0')
+ test_address('8.8.8.8#1')
+ test_address('8.8.8.8#42')
+ test_address('8.8.8.8#888')
+ test_address('8.8.8.8#1337')
+ test_address('8.8.8.8#65535')
+ test_address('8.8.8.8#65536', False)
+ test_address('8.8.8.8#-1', False)
+ test_address('00.0.0.0', False)
+ test_address('010.0.0.0', False)
+ test_address('001.0.0.0', False)
+ test_address('0.0.0.0#00', False)
+ test_address('0.0.0.0#01', False)
+ test_address('0.0.0.0#001', False)
+ test_address('0.0.0.0#0001', False)
+ test_address('0.0.0.0#00001', False)
def test_os_check_fails(Pihole):
''' Confirms install fails on unsupported OS '''
Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ install_dependent_packages ${OS_CHECK_DEPS[@]}
install_dependent_packages ${INSTALLER_DEPS[@]}
cat < /etc/os-release
ID=UnsupportedOS
@@ -586,7 +586,8 @@ def test_os_check_passes(Pihole):
''' Confirms OS meets the requirements '''
Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ install_dependent_packages ${OS_CHECK_DEPS[@]}
install_dependent_packages ${INSTALLER_DEPS[@]}
''')
detectOS = Pihole.run('''
@@ -595,3 +596,44 @@ def test_os_check_passes(Pihole):
''')
expected_stdout = 'Supported OS detected'
assert expected_stdout in detectOS.stdout
+
+
+def test_package_manager_has_installer_deps(Pihole):
+ ''' Confirms OS is able to install the required packages for the installer'''
+ mock_command('whiptail', {'*': ('', '0')}, Pihole)
+ output = Pihole.run('''
+ source /opt/pihole/basic-install.sh
+ package_manager_detect
+ install_dependent_packages ${INSTALLER_DEPS[@]}
+ ''')
+
+ assert 'No package' not in output.stdout # centos7 still exits 0...
+ assert output.rc == 0
+
+
+def test_package_manager_has_pihole_deps(Pihole):
+ ''' Confirms OS is able to install the required packages for Pi-hole '''
+ mock_command('whiptail', {'*': ('', '0')}, Pihole)
+ output = Pihole.run('''
+ source /opt/pihole/basic-install.sh
+ package_manager_detect
+ select_rpm_php
+ install_dependent_packages ${PIHOLE_DEPS[@]}
+ ''')
+
+ assert 'No package' not in output.stdout # centos7 still exits 0...
+ assert output.rc == 0
+
+
+def test_package_manager_has_web_deps(Pihole):
+ ''' Confirms OS is able to install the required packages for web '''
+ mock_command('whiptail', {'*': ('', '0')}, Pihole)
+ output = Pihole.run('''
+ source /opt/pihole/basic-install.sh
+ package_manager_detect
+ select_rpm_php
+ install_dependent_packages ${PIHOLE_WEB_DEPS[@]}
+ ''')
+
+ assert 'No package' not in output.stdout # centos7 still exits 0...
+ assert output.rc == 0
diff --git a/test/test_centos_7_support.py b/test/test_centos_7_support.py
index 2f744ab4..14f62637 100644
--- a/test/test_centos_7_support.py
+++ b/test/test_centos_7_support.py
@@ -9,13 +9,14 @@ def test_php_upgrade_default_optout_centos_eq_7(Pihole):
'''
confirms the default behavior to opt-out of installing PHP7 from REMI
'''
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@@ -27,13 +28,14 @@ def test_php_upgrade_user_optout_centos_eq_7(Pihole):
'''
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@@ -45,16 +47,17 @@ def test_php_upgrade_user_optin_centos_eq_7(Pihole):
'''
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
- assert 'opt-out' not in distro_check.stdout
+ assert 'opt-out' not in package_manager_detect.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert remi_package.is_installed
diff --git a/test/test_centos_8_support.py b/test/test_centos_8_support.py
index d3e83658..bbdbb765 100644
--- a/test/test_centos_8_support.py
+++ b/test/test_centos_8_support.py
@@ -10,13 +10,14 @@ def test_php_upgrade_default_continue_centos_gte_8(Pihole):
confirms the latest version of CentOS continues / does not optout
(should trigger on CentOS7 only)
'''
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.'
' Deprecated PHP may be in use.')
- assert unexpected_stdout not in distro_check.stdout
+ assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@@ -30,13 +31,14 @@ def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole):
'''
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
unexpected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS.'
' Deprecated PHP may be in use.')
- assert unexpected_stdout not in distro_check.stdout
+ assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@@ -50,16 +52,17 @@ def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole):
'''
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
- assert 'opt-out' not in distro_check.stdout
+ assert 'opt-out' not in package_manager_detect.stdout
unexpected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
- assert unexpected_stdout not in distro_check.stdout
+ assert unexpected_stdout not in package_manager_detect.stdout
unexpected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
- assert unexpected_stdout not in distro_check.stdout
+ assert unexpected_stdout not in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
diff --git a/test/test_centos_common_support.py b/test/test_centos_common_support.py
index fdf43cba..0b36cbae 100644
--- a/test/test_centos_common_support.py
+++ b/test/test_centos_common_support.py
@@ -13,29 +13,31 @@ def test_release_supported_version_check_centos(Pihole):
'''
# modify /etc/redhat-release to mock an unsupported CentOS release
Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release')
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
expected_stdout = cross_box + (' CentOS 6 is not supported.')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
expected_stdout = 'Please update to CentOS release 7 or later'
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
def test_enable_epel_repository_centos(Pihole):
'''
confirms the EPEL package repository is enabled when installed on CentOS
'''
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
expected_stdout = info_box + (' Enabling EPEL package repository '
'(https://fedoraproject.org/wiki/EPEL)')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + ' Installed epel-release'
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
epel_package = Pihole.package('epel-release')
assert epel_package.is_installed
@@ -51,13 +53,14 @@ def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole):
default_centos_php_version = php_package.version.split('.')[0]
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
pytest.skip("Test deprecated . Detected default PHP version >= 7")
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@@ -75,13 +78,14 @@ def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole):
pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole)
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert not remi_package.is_installed
@@ -99,20 +103,21 @@ def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole):
pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole)
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
install_dependent_packages PIHOLE_WEB_DEPS[@]
''')
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.')
- assert expected_stdout not in distro_check.stdout
+ assert expected_stdout not in package_manager_detect.stdout
expected_stdout = info_box + (' Enabling Remi\'s RPM repository '
'(https://rpms.remirepo.net)')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7')
- assert expected_stdout in distro_check.stdout
+ assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release')
assert remi_package.is_installed
updated_php_package = Pihole.package('php')
diff --git a/test/test_fedora_support.py b/test/test_fedora_support.py
index 473b2e96..3ad84be5 100644
--- a/test/test_fedora_support.py
+++ b/test/test_fedora_support.py
@@ -3,11 +3,12 @@ def test_epel_and_remi_not_installed_fedora(Pihole):
confirms installer does not attempt to install EPEL/REMI repositories
on Fedora
'''
- distro_check = Pihole.run('''
+ package_manager_detect = Pihole.run('''
source /opt/pihole/basic-install.sh
- distro_check
+ package_manager_detect
+ select_rpm_php
''')
- assert distro_check.stdout == ''
+ assert package_manager_detect.stdout == ''
epel_package = Pihole.package('epel-release')
assert not epel_package.is_installed
diff --git a/test/tox.debian_11.ini b/test/tox.debian_11.ini
new file mode 100644
index 00000000..af3c6e36
--- /dev/null
+++ b/test/tox.debian_11.ini
@@ -0,0 +1,8 @@
+[tox]
+envlist = py37
+
+[testenv]
+whitelist_externals = docker
+deps = -rrequirements.txt
+commands = docker build -f _debian_11.Dockerfile -t pytest_pihole:test_container ../
+ pytest {posargs:-vv -n auto} ./test_automated_install.py
diff --git a/test/tox.fedora_31.ini b/test/tox.fedora_33.ini
similarity index 78%
rename from test/tox.fedora_31.ini
rename to test/tox.fedora_33.ini
index 36ab10ad..00ea732a 100644
--- a/test/tox.fedora_31.ini
+++ b/test/tox.fedora_33.ini
@@ -4,5 +4,5 @@ envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
-commands = docker build -f _fedora_31.Dockerfile -t pytest_pihole:test_container ../
+commands = docker build -f _fedora_33.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py
diff --git a/test/tox.fedora_32.ini b/test/tox.fedora_34.ini
similarity index 78%
rename from test/tox.fedora_32.ini
rename to test/tox.fedora_34.ini
index c68e0757..154662cf 100644
--- a/test/tox.fedora_32.ini
+++ b/test/tox.fedora_34.ini
@@ -4,5 +4,5 @@ envlist = py37
[testenv]
whitelist_externals = docker
deps = -rrequirements.txt
-commands = docker build -f _fedora_32.Dockerfile -t pytest_pihole:test_container ../
+commands = docker build -f _fedora_34.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_automated_install.py ./test_centos_fedora_common_support.py ./test_fedora_support.py
diff --git a/test/tox.ubuntu_21.ini b/test/tox.ubuntu_21.ini
new file mode 100644
index 00000000..651f933b
--- /dev/null
+++ b/test/tox.ubuntu_21.ini
@@ -0,0 +1,8 @@
+[tox]
+envlist = py37
+
+[testenv]
+whitelist_externals = docker
+deps = -rrequirements.txt
+commands = docker build -f _ubuntu_21.Dockerfile -t pytest_pihole:test_container ../
+ pytest {posargs:-vv -n auto} ./test_automated_install.py