Merge pull request #4480 from pi-hole/development

Pi-hole Core v5.7
pull/4513/head v5.7
Adam Warner 2 years ago committed by GitHub
commit 1b809e4e8e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -36,10 +36,10 @@ jobs:
name: Checkout repository name: Checkout repository
uses: actions/checkout@v2 uses: actions/checkout@v2
- -
name: Set up Python 3.7 name: Set up Python 3.8
uses: actions/setup-python@v2 uses: actions/setup-python@v2
with: with:
python-version: 3.7 python-version: 3.8
- -
name: Install dependencies name: Install dependencies
run: pip install -r test/requirements.txt run: pip install -r test/requirements.txt

1
.gitignore vendored

@ -9,3 +9,4 @@ __pycache__
*.egg-info *.egg-info
.idea/ .idea/
*.iml *.iml
.vscode/

@ -2,111 +2,6 @@
Please read and understand the contribution guide before creating an issue or pull request. Please read and understand the contribution guide before creating an issue or pull request.
## Etiquette The guide can be found here: [https://docs.pi-hole.net/guides/github/contributing/](https://docs.pi-hole.net/guides/github/contributing/)
- Our goal for Pi-hole is **stability before features**. This means we focus on squashing critical bugs before adding new features. Often, we can do both in tandem, but bugs will take priority over a new feature.
- Pi-hole is open source and [powered by donations](https://pi-hole.net/donate/), and as such, we give our **free time** to build, maintain, and **provide user support** for this project. It would be extremely unfair for us to suffer abuse or anger for our hard work, so please take a moment to consider that.
- Please be considerate towards the developers and other users when raising issues or presenting pull requests.
- Respect our decision(s), and do not be upset or abusive if your submission is not used.
## Viability
When requesting or submitting new features, first consider whether it might be useful to others. Open source projects are used by many people, who may have entirely different needs to your own. Think about whether or not your feature is likely to be used by other users of the project.
## Procedure
**Before filing an issue:**
- Attempt to replicate and **document** the problem, to ensure that it wasn't a coincidental incident.
- Check to make sure your feature suggestion isn't already present within the project.
- Check the pull requests tab to ensure that the bug doesn't have a fix in progress.
- Check the pull requests tab to ensure that the feature isn't already in progress.
**Before submitting a pull request:**
- Check the codebase to ensure that your feature doesn't already exist.
- Check the pull requests to ensure that another person hasn't already submitted the feature or fix.
- Read and understand the [DCO guidelines](https://docs.pi-hole.net/guides/github/contributing/) for the project.
## Technical Requirements
- Submit Pull Requests to the **development branch only**.
- Before Submitting your Pull Request, merge `development` with your new branch and fix any conflicts. (Make sure you don't break anything in development!)
- Please use the [Google Style Guide for Shell](https://google.github.io/styleguide/shell.xml) for your code submission styles.
- Commit Unix line endings.
- Please use the Pi-hole brand: **Pi-hole** (Take a special look at the capitalized 'P' and a low 'h' with a hyphen)
- (Optional fun) keep to the theme of Star Trek/black holes/gravity.
## Forking and Cloning from GitHub to GitHub
1. Fork <https://github.com/pi-hole/pi-hole/> to a repo under a namespace you control, or have permission to use, for example: `https://github.com/<your_namespace>/<your_repo_name>/`. You can do this from the github.com website.
2. Clone `https://github.com/<your_namespace>/<your_repo_name>/` with the tool of you choice.
3. To keep your fork in sync with our repo, add an upstream remote for pi-hole/pi-hole to your repo.
```bash
git remote add upstream https://github.com/pi-hole/pi-hole.git
```
4. Checkout the `development` branch from your fork `https://github.com/<your_namespace>/<your_repo_name>/`.
5. Create a topic/branch, based on the `development` branch code. *Bonus fun to keep to the theme of Star Trek/black holes/gravity.*
6. Make your changes and commit to your topic branch in your repo.
7. Rebase your commits and squash any insignificant commits. See the notes below for an example.
8. Merge `development` your branch and fix any conflicts.
9. Open a Pull Request to merge your topic branch into our repo's `development` branch.
- Keep in mind the technical requirements from above.
## Forking and Cloning from GitHub to other code hosting sites
- Forking is a GitHub concept and cannot be done from GitHub to other git-based code hosting sites. However, those sites may be able to mirror a GitHub repo.
1. To contribute from another code hosting site, you must first complete the steps above to fork our repo to a GitHub namespace you have permission to use, for example: `https://github.com/<your_namespace>/<your_repo_name>/`.
2. Create a repo in your code hosting site, for example: `https://gitlab.com/<your_namespace>/<your_repo_name>/`
3. Follow the instructions from your code hosting site to create a mirror between `https://github.com/<your_namespace>/<your_repo_name>/` and `https://gitlab.com/<your_namespace>/<your_repo_name>/`.
4. When you are ready to create a Pull Request (PR), follow the steps `(starting at step #6)` from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github) and create the PR from `https://github.com/<your_namespace>/<your_repo_name>/`.
## Notes for squashing commits with rebase
- To rebase your commits and squash previous commits, you can use:
```bash
git rebase -i your_topic_branch~(number of commits to combine)
```
- For more details visit [gitready.com](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html)
1. The following would combine the last four commits in the branch `mytopic`.
```bash
git rebase -i mytopic~4
```
2. An editor window opens with the most recent commits indicated: (edit the commands to the left of the commit ID)
```gitattributes
pick 9dff55b2 existing commit comments
squash ebb1a730 existing commit comments
squash 07cc5b50 existing commit comments
reword 9dff55b2 existing commit comments
```
3. Save and close the editor. The next editor window opens: (edit the new commit message). *If you select reword for a commit, an additional editor window will open for you to edit the comment.*
```bash
new commit comments
Signed-off-by: yourname <your email address>
```
4. Save and close the editor for the rebase process to execute. The terminal output should say something like the following:
```bash
Successfully rebased and updated refs/heads/mytopic.
```
5. Once you have a successful rebase, and before you sync your local clone, you have to force push origin to update your repo:
```bash
git push -f origin
```
6. Continue on from step #7 from [Forking and Cloning from GitHub to GitHub](#forking-and-cloning-from-github-to-github)

@ -25,11 +25,12 @@ server=/localhost/
server=/invalid/ server=/invalid/
# The same RFC requests something similar for # The same RFC requests something similar for
# 16.172.in-addr.arpa. 22.172.in-addr.arpa. 27.172.in-addr.arpa. # 10.in-addr.arpa. 21.172.in-addr.arpa. 27.172.in-addr.arpa.
# 17.172.in-addr.arpa. 30.172.in-addr.arpa. 28.172.in-addr.arpa. # 16.172.in-addr.arpa. 22.172.in-addr.arpa. 28.172.in-addr.arpa.
# 18.172.in-addr.arpa. 23.172.in-addr.arpa. 29.172.in-addr.arpa. # 17.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. # 18.172.in-addr.arpa. 24.172.in-addr.arpa. 30.172.in-addr.arpa.
# 20.172.in-addr.arpa. 25.172.in-addr.arpa. 168.192.in-addr.arpa. # 19.172.in-addr.arpa. 25.172.in-addr.arpa. 31.172.in-addr.arpa.
# 20.172.in-addr.arpa. 26.172.in-addr.arpa. 168.192.in-addr.arpa.
# Pi-hole implements this via the dnsmasq option "bogus-priv" (see # Pi-hole implements this via the dnsmasq option "bogus-priv" (see
# 01-pihole.conf) because this also covers IPv6. # 01-pihole.conf) because this also covers IPv6.

@ -329,8 +329,8 @@ get_sys_stats() {
*) cpu_col="$COL_URG_RED";; *) cpu_col="$COL_URG_RED";;
esac esac
# $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED # $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED
cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
elif [[ "$temp_unit" == "F" ]]; then elif [[ "$temp_unit" == "F" ]]; then
cpu_temp=$(printf "%.0ff\\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")") cpu_temp=$(printf "%.0ff\\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")")
@ -445,7 +445,7 @@ get_strings() {
lan_info="Gateway: $net_gateway" lan_info="Gateway: $net_gateway"
dhcp_info="$leased_str$ph_dhcp_num of $ph_dhcp_max" dhcp_info="$leased_str$ph_dhcp_num of $ph_dhcp_max"
ads_info="$total_str$ads_blocked_today of $dns_queries_today" ads_info="$total_str$ads_blocked_today of $dns_queries_today"
dns_info="$dns_count DNS servers" dns_info="$dns_count DNS servers"
[[ "$recent_blocked" == "0" ]] && recent_blocked="${COL_LIGHT_RED}FTL offline${COL_NC}" [[ "$recent_blocked" == "0" ]] && recent_blocked="${COL_LIGHT_RED}FTL offline${COL_NC}"
@ -488,7 +488,7 @@ chronoFunc() {
${COL_LIGHT_RED}Press Ctrl-C to exit${COL_NC} ${COL_LIGHT_RED}Press Ctrl-C to exit${COL_NC}
${COL_DARK_GRAY}$scr_line_str${COL_NC}" ${COL_DARK_GRAY}$scr_line_str${COL_NC}"
else else
echo -e "|¯¯¯(¯)_|¯|_ ___|¯|___$phc_ver_str\\n| ¯_/¯|_| ' \\/ _ \\ / -_)$lte_ver_str\\n|_| |_| |_||_\\___/_\\___|$ftl_ver_str\\n ${COL_DARK_GRAY}$scr_line_str${COL_NC}" echo -e "|¯¯¯(¯)_|¯|_ ___|¯|___$phc_ver_str\\n| ¯_/¯|_| ' \\/ _ \\ / -_)$lte_ver_str\\n|_| |_| |_||_\\___/_\\___|$ftl_ver_str\\n ${COL_DARK_GRAY}$scr_line_str${COL_NC}"
fi fi
printFunc " Hostname: " "$sys_name" "$host_info" printFunc " Hostname: " "$sys_name" "$host_info"

@ -123,9 +123,9 @@ upgrade_gravityDB(){
version=14 version=14
fi fi
if [[ "$version" == "14" ]]; then if [[ "$version" == "14" ]]; then
# Changes the vw_adlist created in 5_to_6 # Changes the vw_adlist created in 5_to_6
echo -e " ${INFO} Upgrading gravity database from version 14 to 15" echo -e " ${INFO} Upgrading gravity database from version 14 to 15"
sqlite3 "${database}" < "${scriptPath}/14_to_15.sql" sqlite3 "${database}" < "${scriptPath}/14_to_15.sql"
version=15 version=15
fi fi
} }

@ -16,7 +16,7 @@ GRAVITYDB="${piholeDir}/gravity.db"
# Source pihole-FTL from install script # Source pihole-FTL from install script
pihole_FTL="${piholeDir}/pihole-FTL.conf" pihole_FTL="${piholeDir}/pihole-FTL.conf"
if [[ -f "${pihole_FTL}" ]]; then if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}" source "${pihole_FTL}"
fi fi
# Set this only after sourcing pihole-FTL.conf as the gravity database path may # Set this only after sourcing pihole-FTL.conf as the gravity database path may
@ -91,7 +91,8 @@ Options:
-q, --quiet Make output less verbose -q, --quiet Make output less verbose
-h, --help Show this help dialog -h, --help Show this help dialog
-l, --list Display all your ${listname}listed domains -l, --list Display all your ${listname}listed domains
--nuke Removes all entries in a list" --nuke Removes all entries in a list
--comment \"text\" Add a comment to the domain. If adding multiple domains the same comment will be used for all"
exit 0 exit 0
} }
@ -133,7 +134,7 @@ ProcessDomainList() {
else else
RemoveDomain "${dom}" RemoveDomain "${dom}"
fi fi
done done
} }
AddDomain() { AddDomain() {
@ -145,19 +146,19 @@ AddDomain() {
requestedListname="$(GetListnameFromTypeId "${typeId}")" requestedListname="$(GetListnameFromTypeId "${typeId}")"
if [[ "${num}" -ne 0 ]]; then if [[ "${num}" -ne 0 ]]; then
existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")" existingTypeId="$(sqlite3 "${gravityDBfile}" "SELECT type FROM domainlist WHERE domain = '${domain}';")"
if [[ "${existingTypeId}" == "${typeId}" ]]; then if [[ "${existingTypeId}" == "${typeId}" ]]; then
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!" echo -e " ${INFO} ${1} already exists in ${requestedListname}, no need to add!"
fi fi
else else
existingListname="$(GetListnameFromTypeId "${existingTypeId}")" existingListname="$(GetListnameFromTypeId "${existingTypeId}")"
sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';" sqlite3 "${gravityDBfile}" "UPDATE domainlist SET type = ${typeId} WHERE domain='${domain}';"
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!" echo -e " ${INFO} ${1} already exists in ${existingListname}, it has been moved to ${requestedListname}!"
fi
fi fi
fi return
return
fi fi
# Domain not found in the table, add it! # Domain not found in the table, add it!
@ -185,10 +186,10 @@ RemoveDomain() {
requestedListname="$(GetListnameFromTypeId "${typeId}")" requestedListname="$(GetListnameFromTypeId "${typeId}")"
if [[ "${num}" -eq 0 ]]; then if [[ "${num}" -eq 0 ]]; then
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!" echo -e " ${INFO} ${domain} does not exist in ${requestedListname}, no need to remove!"
fi fi
return return
fi fi
# Domain found in the table, remove it! # Domain found in the table, remove it!
@ -256,8 +257,8 @@ NukeList() {
GetComment() { GetComment() {
comment="$1" comment="$1"
if [[ "${comment}" =~ [^a-zA-Z0-9_\#:/\.,\ -] ]]; then if [[ "${comment}" =~ [^a-zA-Z0-9_\#:/\.,\ -] ]]; then
echo " ${CROSS} Found invalid characters in domain comment!" echo " ${CROSS} Found invalid characters in domain comment!"
exit exit
fi fi
} }
@ -292,7 +293,7 @@ ProcessDomainList
# Used on web interface # Used on web interface
if $web; then if $web; then
echo "DONE" echo "DONE"
fi fi
if [[ ${reload} == true && ${noReloadRequested} == false ]]; then if [[ ${reload} == true && ${noReloadRequested} == false ]]; then

@ -27,7 +27,7 @@ PIHOLE_COLTABLE_FILE="${PIHOLE_SCRIPTS_DIRECTORY}/COL_TABLE"
# These provide the colors we need for making the log more readable # These provide the colors we need for making the log more readable
if [[ -f ${PIHOLE_COLTABLE_FILE} ]]; then if [[ -f ${PIHOLE_COLTABLE_FILE} ]]; then
source ${PIHOLE_COLTABLE_FILE} source ${PIHOLE_COLTABLE_FILE}
else else
COL_NC='\e[0m' # No Color COL_NC='\e[0m' # No Color
COL_RED='\e[1;91m' COL_RED='\e[1;91m'
@ -88,6 +88,7 @@ PIHOLE_LOCAL_HOSTS_FILE="${PIHOLE_DIRECTORY}/local.list"
PIHOLE_LOGROTATE_FILE="${PIHOLE_DIRECTORY}/logrotate" PIHOLE_LOGROTATE_FILE="${PIHOLE_DIRECTORY}/logrotate"
PIHOLE_SETUP_VARS_FILE="${PIHOLE_DIRECTORY}/setupVars.conf" PIHOLE_SETUP_VARS_FILE="${PIHOLE_DIRECTORY}/setupVars.conf"
PIHOLE_FTL_CONF_FILE="${PIHOLE_DIRECTORY}/pihole-FTL.conf" PIHOLE_FTL_CONF_FILE="${PIHOLE_DIRECTORY}/pihole-FTL.conf"
PIHOLE_CUSTOM_HOSTS_FILE="${PIHOLE_DIRECTORY}/custom.list"
# Read the value of an FTL config key. The value is printed to stdout. # Read the value of an FTL config key. The value is printed to stdout.
# #
@ -179,7 +180,8 @@ REQUIRED_FILES=("${PIHOLE_CRON_FILE}"
"${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE}" "${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE}"
"${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}" "${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}"
"${RESOLVCONF}" "${RESOLVCONF}"
"${DNSMASQ_CONF}") "${DNSMASQ_CONF}"
"${PIHOLE_CUSTOM_HOSTS_FILE}")
DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net. DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net.

@ -21,7 +21,7 @@ matchType="match"
# Source pihole-FTL from install script # Source pihole-FTL from install script
pihole_FTL="${piholeDir}/pihole-FTL.conf" pihole_FTL="${piholeDir}/pihole-FTL.conf"
if [[ -f "${pihole_FTL}" ]]; then if [[ -f "${pihole_FTL}" ]]; then
source "${pihole_FTL}" source "${pihole_FTL}"
fi fi
# Set this only after sourcing pihole-FTL.conf as the gravity database path may # Set this only after sourcing pihole-FTL.conf as the gravity database path may
@ -48,7 +48,7 @@ scanList(){
# Iterate through each regexp and check whether it matches the domainQuery # Iterate through each regexp and check whether it matches the domainQuery
# If it does, print the matching regexp and continue looping # If it does, print the matching regexp and continue looping
# Input 1 - regexps | Input 2 - domainQuery # Input 1 - regexps | Input 2 - domainQuery
"regex" ) "regex" )
for list in ${lists}; do for list in ${lists}; do
if [[ "${domain}" =~ ${list} ]]; then if [[ "${domain}" =~ ${list} ]]; then
printf "%b\n" "${list}"; printf "%b\n" "${list}";
@ -109,15 +109,15 @@ scanDatabaseTable() {
# behavior. The "ESCAPE '\'" clause specifies that an underscore preceded by an '\' should be matched # behavior. The "ESCAPE '\'" clause specifies that an underscore preceded by an '\' should be matched
# as a literal underscore character. We pretreat the $domain variable accordingly to escape underscores. # as a literal underscore character. We pretreat the $domain variable accordingly to escape underscores.
if [[ "${table}" == "gravity" ]]; then if [[ "${table}" == "gravity" ]]; then
case "${exact}" in case "${exact}" in
"exact" ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain = '${domain}'";; "exact" ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain = '${domain}'";;
* ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";; * ) querystr="SELECT gravity.domain,adlist.address,adlist.enabled FROM gravity LEFT JOIN adlist ON adlist.id = gravity.adlist_id WHERE domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";;
esac esac
else else
case "${exact}" in case "${exact}" in
"exact" ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain = '${domain}'";; "exact" ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain = '${domain}'";;
* ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";; * ) querystr="SELECT domain,enabled FROM domainlist WHERE type = '${type}' AND domain LIKE '%${domain//_/\\_}%' ESCAPE '\\'";;
esac esac
fi fi
# Send prepared query to gravity database # Send prepared query to gravity database
@ -128,8 +128,8 @@ scanDatabaseTable() {
fi fi
if [[ "${table}" == "gravity" ]]; then if [[ "${table}" == "gravity" ]]; then
echo "${result}" echo "${result}"
return return
fi fi
# Mark domain as having been white-/blacklist matched (global variable) # Mark domain as having been white-/blacklist matched (global variable)
@ -233,9 +233,9 @@ for result in "${results[@]}"; do
adlistAddress="${extra/|*/}" adlistAddress="${extra/|*/}"
extra="${extra#*|}" extra="${extra#*|}"
if [[ "${extra}" == "0" ]]; then if [[ "${extra}" == "0" ]]; then
extra="(disabled)" extra="(disabled)"
else else
extra="" extra=""
fi fi
if [[ -n "${blockpage}" ]]; then if [[ -n "${blockpage}" ]]; then

@ -35,25 +35,37 @@ source "/opt/pihole/COL_TABLE"
GitCheckUpdateAvail() { GitCheckUpdateAvail() {
local directory local directory
local curBranch
directory="${1}" directory="${1}"
curdir=$PWD curdir=$PWD
cd "${directory}" || return cd "${directory}" || return
# Fetch latest changes in this repo # Fetch latest changes in this repo
git fetch --quiet origin git fetch --tags --quiet origin
# @ alone is a shortcut for HEAD. Older versions of git # Check current branch. If it is master, then check for the latest available tag instead of latest commit.
# need @{0} curBranch=$(git rev-parse --abbrev-ref HEAD)
LOCAL="$(git rev-parse "@{0}")" if [[ "${curBranch}" == "master" ]]; then
# get the latest local tag
LOCAL=$(git describe --abbrev=0 --tags master)
# get the latest tag from remote
REMOTE=$(git describe --abbrev=0 --tags origin/master)
else
# @ alone is a shortcut for HEAD. Older versions of git
# need @{0}
LOCAL="$(git rev-parse "@{0}")"
# The suffix @{upstream} to a branchname
# (short form <branchname>@{u}) refers
# to the branch that the branch specified
# by branchname is set to build on top of#
# (configured with branch.<name>.remote and
# branch.<name>.merge). A missing branchname
# defaults to the current one.
REMOTE="$(git rev-parse "@{upstream}")"
fi
# The suffix @{upstream} to a branchname
# (short form <branchname>@{u}) refers
# to the branch that the branch specified
# by branchname is set to build on top of#
# (configured with branch.<name>.remote and
# branch.<name>.merge). A missing branchname
# defaults to the current one.
REMOTE="$(git rev-parse "@{upstream}")"
if [[ "${#LOCAL}" == 0 ]]; then if [[ "${#LOCAL}" == 0 ]]; then
echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support" echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support"
@ -200,7 +212,7 @@ main() {
if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then if [[ "${FTL_update}" == true || "${core_update}" == true ]]; then
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \ ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
echo -e "${basicError}" && exit 1 echo -e "${basicError}" && exit 1
fi fi
if [[ "${FTL_update}" == true || "${core_update}" == true || "${web_update}" == true ]]; then if [[ "${FTL_update}" == true || "${core_update}" == true || "${web_update}" == true ]]; then

@ -13,6 +13,10 @@ DEFAULT="-1"
COREGITDIR="/etc/.pihole/" COREGITDIR="/etc/.pihole/"
WEBGITDIR="/var/www/html/admin/" WEBGITDIR="/var/www/html/admin/"
# Source the setupvars config file
# shellcheck disable=SC1091
source /etc/pihole/setupVars.conf
getLocalVersion() { getLocalVersion() {
# FTL requires a different method # FTL requires a different method
if [[ "$1" == "FTL" ]]; then if [[ "$1" == "FTL" ]]; then
@ -91,10 +95,11 @@ getRemoteVersion(){
#If the above file exists, then we can read from that. Prevents overuse of GitHub API #If the above file exists, then we can read from that. Prevents overuse of GitHub API
if [[ -f "$cachedVersions" ]]; then if [[ -f "$cachedVersions" ]]; then
IFS=' ' read -r -a arrCache < "$cachedVersions" IFS=' ' read -r -a arrCache < "$cachedVersions"
case $daemon in case $daemon in
"pi-hole" ) echo "${arrCache[0]}";; "pi-hole" ) echo "${arrCache[0]}";;
"AdminLTE" ) echo "${arrCache[1]}";; "AdminLTE" ) [[ "${INSTALL_WEB_INTERFACE}" == true ]] && echo "${arrCache[1]}";;
"FTL" ) echo "${arrCache[2]}";; "FTL" ) [[ "${INSTALL_WEB_INTERFACE}" == true ]] && echo "${arrCache[2]}" || echo "${arrCache[1]}";;
esac esac
return 0 return 0
@ -117,7 +122,7 @@ getLocalBranch(){
local directory="${1}" local directory="${1}"
local branch local branch
# Local FTL btranch is stored in /etc/pihole/ftlbranch # Local FTL btranch is stored in /etc/pihole/ftlbranch
if [[ "$1" == "FTL" ]]; then if [[ "$1" == "FTL" ]]; then
branch="$(pihole-FTL branch)" branch="$(pihole-FTL branch)"
else else
@ -140,6 +145,11 @@ getLocalBranch(){
} }
versionOutput() { versionOutput() {
if [[ "$1" == "AdminLTE" && "${INSTALL_WEB_INTERFACE}" != true ]]; then
echo " WebAdmin not installed"
return 1
fi
[[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR [[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR
[[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR [[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR
[[ "$1" == "FTL" ]] && GITDIR="FTL" [[ "$1" == "FTL" ]] && GITDIR="FTL"
@ -166,6 +176,7 @@ versionOutput() {
output="Latest ${1^} hash is $latHash" output="Latest ${1^} hash is $latHash"
else else
errorOutput errorOutput
return 1
fi fi
[[ -n "$output" ]] && echo " $output" [[ -n "$output" ]] && echo " $output"
@ -177,10 +188,6 @@ errorOutput() {
} }
defaultOutput() { defaultOutput() {
# Source the setupvars config file
# shellcheck disable=SC1091
source /etc/pihole/setupVars.conf
versionOutput "pi-hole" "$@" versionOutput "pi-hole" "$@"
if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then

@ -122,14 +122,14 @@ SetWebPassword() {
read -s -r -p "Enter New Password (Blank for no password): " PASSWORD read -s -r -p "Enter New Password (Blank for no password): " PASSWORD
echo "" echo ""
if [ "${PASSWORD}" == "" ]; then if [ "${PASSWORD}" == "" ]; then
change_setting "WEBPASSWORD" "" change_setting "WEBPASSWORD" ""
echo -e " ${TICK} Password Removed" echo -e " ${TICK} Password Removed"
exit 0 exit 0
fi fi
read -s -r -p "Confirm Password: " CONFIRM read -s -r -p "Confirm Password: " CONFIRM
echo "" echo ""
fi fi
if [ "${PASSWORD}" == "${CONFIRM}" ] ; then if [ "${PASSWORD}" == "${CONFIRM}" ] ; then
@ -199,6 +199,8 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
# Setup interface listening behavior of dnsmasq # Setup interface listening behavior of dnsmasq
delete_dnsmasq_setting "interface" delete_dnsmasq_setting "interface"
delete_dnsmasq_setting "local-service" delete_dnsmasq_setting "local-service"
delete_dnsmasq_setting "except-interface"
delete_dnsmasq_setting "bind-interfaces"
if [[ "${DNSMASQ_LISTENING}" == "all" ]]; then if [[ "${DNSMASQ_LISTENING}" == "all" ]]; then
# Listen on all interfaces, permit all origins # Listen on all interfaces, permit all origins
@ -207,6 +209,7 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
# Listen only on all interfaces, but only local subnets # Listen only on all interfaces, but only local subnets
add_dnsmasq_setting "local-service" add_dnsmasq_setting "local-service"
else else
# Options "bind" and "single"
# Listen only on one interface # Listen only on one interface
# Use eth0 as fallback interface if interface is missing in setupVars.conf # Use eth0 as fallback interface if interface is missing in setupVars.conf
if [ -z "${PIHOLE_INTERFACE}" ]; then if [ -z "${PIHOLE_INTERFACE}" ]; then
@ -214,6 +217,11 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
fi fi
add_dnsmasq_setting "interface" "${PIHOLE_INTERFACE}" add_dnsmasq_setting "interface" "${PIHOLE_INTERFACE}"
if [[ "${DNSMASQ_LISTENING}" == "bind" ]]; then
# Really bind to interface
add_dnsmasq_setting "bind-interfaces"
fi
fi fi
if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then if [[ "${CONDITIONAL_FORWARDING}" == true ]]; then
@ -247,8 +255,8 @@ trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC68345710423
3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";; 3 ) REV_SERVER_CIDR="${arrRev[0]}.0.0.0/8";;
esac esac
else else
# Set REV_SERVER_CIDR to whatever value it was set to # Set REV_SERVER_CIDR to whatever value it was set to
REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}" REV_SERVER_CIDR="${CONDITIONAL_FORWARDING_REVERSE}"
fi fi
# If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it # If REV_SERVER_CIDR is not converted by the above, then use the REV_SERVER_TARGET variable to derive it
@ -371,34 +379,34 @@ ProcessDHCPSettings() {
source "${setupVars}" source "${setupVars}"
if [[ "${DHCP_ACTIVE}" == "true" ]]; then if [[ "${DHCP_ACTIVE}" == "true" ]]; then
interface="${PIHOLE_INTERFACE}" interface="${PIHOLE_INTERFACE}"
# Use eth0 as fallback interface # Use eth0 as fallback interface
if [ -z ${interface} ]; then if [ -z ${interface} ]; then
interface="eth0" interface="eth0"
fi fi
if [[ "${PIHOLE_DOMAIN}" == "" ]]; then if [[ "${PIHOLE_DOMAIN}" == "" ]]; then
PIHOLE_DOMAIN="lan" PIHOLE_DOMAIN="lan"
change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}"
fi fi
if [[ "${DHCP_LEASETIME}" == "0" ]]; then if [[ "${DHCP_LEASETIME}" == "0" ]]; then
leasetime="infinite" leasetime="infinite"
elif [[ "${DHCP_LEASETIME}" == "" ]]; then elif [[ "${DHCP_LEASETIME}" == "" ]]; then
leasetime="24" leasetime="24"
change_setting "DHCP_LEASETIME" "${leasetime}" change_setting "DHCP_LEASETIME" "${leasetime}"
elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then
#Installation is affected by known bug, introduced in a previous version. #Installation is affected by known bug, introduced in a previous version.
#This will automatically clean up setupVars.conf and remove the unnecessary "h" #This will automatically clean up setupVars.conf and remove the unnecessary "h"
leasetime="24" leasetime="24"
change_setting "DHCP_LEASETIME" "${leasetime}" change_setting "DHCP_LEASETIME" "${leasetime}"
else else
leasetime="${DHCP_LEASETIME}h" leasetime="${DHCP_LEASETIME}h"
fi fi
# Write settings to file # Write settings to file
echo "############################################################################### echo "###############################################################################
# DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE. # # DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE. #
# ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE # # ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE #
############################################################################### ###############################################################################
@ -408,34 +416,34 @@ dhcp-option=option:router,${DHCP_ROUTER}
dhcp-leasefile=/etc/pihole/dhcp.leases dhcp-leasefile=/etc/pihole/dhcp.leases
#quiet-dhcp #quiet-dhcp
" > "${dhcpconfig}" " > "${dhcpconfig}"
chmod 644 "${dhcpconfig}" chmod 644 "${dhcpconfig}"
if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
# When there is a Pi-hole domain set and "Never forward non-FQDNs" is # When there is a Pi-hole domain set and "Never forward non-FQDNs" is
# ticked, we add `local=/domain/` to tell FTL that this domain is purely # ticked, we add `local=/domain/` to tell FTL that this domain is purely
# local and FTL may answer queries from /etc/hosts or DHCP but should # local and FTL may answer queries from /etc/hosts or DHCP but should
# never forward queries on that domain to any upstream servers # never forward queries on that domain to any upstream servers
if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
echo "local=/${PIHOLE_DOMAIN}/" >> "${dhcpconfig}" echo "local=/${PIHOLE_DOMAIN}/" >> "${dhcpconfig}"
fi
fi fi
fi
# Sourced from setupVars # Sourced from setupVars
# shellcheck disable=SC2154 # shellcheck disable=SC2154
if [[ "${DHCP_rapid_commit}" == "true" ]]; then if [[ "${DHCP_rapid_commit}" == "true" ]]; then
echo "dhcp-rapid-commit" >> "${dhcpconfig}" echo "dhcp-rapid-commit" >> "${dhcpconfig}"
fi fi
if [[ "${DHCP_IPv6}" == "true" ]]; then if [[ "${DHCP_IPv6}" == "true" ]]; then
echo "#quiet-dhcp6 echo "#quiet-dhcp6
#enable-ra #enable-ra
dhcp-option=option6:dns-server,[::] dhcp-option=option6:dns-server,[::]
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,64,3600 dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,64,3600
ra-param=*,0,0 ra-param=*,0,0
" >> "${dhcpconfig}" " >> "${dhcpconfig}"
fi fi
else else
if [[ -f "${dhcpconfig}" ]]; then if [[ -f "${dhcpconfig}" ]]; then
@ -532,25 +540,6 @@ CustomizeAdLists() {
fi fi
} }
SetPrivacyMode() {
if [[ "${args[2]}" == "true" ]]; then
change_setting "API_PRIVACY_MODE" "true"
else
change_setting "API_PRIVACY_MODE" "false"
fi
}
ResolutionSettings() {
typ="${args[2]}"
state="${args[3]}"
if [[ "${typ}" == "forward" ]]; then
change_setting "API_GET_UPSTREAM_DNS_HOSTNAME" "${state}"
elif [[ "${typ}" == "clients" ]]; then
change_setting "API_GET_CLIENT_HOSTNAME" "${state}"
fi
}
AddDHCPStaticAddress() { AddDHCPStaticAddress() {
mac="${args[2]}" mac="${args[2]}"
ip="${args[3]}" ip="${args[3]}"
@ -619,12 +608,13 @@ Example: 'pihole -a -i local'
Specify dnsmasq's network interface listening behavior Specify dnsmasq's network interface listening behavior
Interfaces: Interfaces:
local Listen on all interfaces, but only allow queries from local Only respond to queries from devices that
devices that are at most one hop away (local devices) are at most one hop away (local devices)
single Listen only on ${PIHOLE_INTERFACE} interface single Respond only on interface ${PIHOLE_INTERFACE}
bind Bind only on interface ${PIHOLE_INTERFACE}
all Listen on all interfaces, permit all origins" all Listen on all interfaces, permit all origins"
exit 0 exit 0
fi fi
if [[ "${args[2]}" == "all" ]]; then if [[ "${args[2]}" == "all" ]]; then
echo -e " ${INFO} Listening on all interfaces, permitting all origins. Please use a firewall!" echo -e " ${INFO} Listening on all interfaces, permitting all origins. Please use a firewall!"
@ -632,6 +622,9 @@ Interfaces:
elif [[ "${args[2]}" == "local" ]]; then elif [[ "${args[2]}" == "local" ]]; then
echo -e " ${INFO} Listening on all interfaces, permitting origins from one hop away (LAN)" echo -e " ${INFO} Listening on all interfaces, permitting origins from one hop away (LAN)"
change_setting "DNSMASQ_LISTENING" "local" change_setting "DNSMASQ_LISTENING" "local"
elif [[ "${args[2]}" == "bind" ]]; then
echo -e " ${INFO} Binding on interface ${PIHOLE_INTERFACE}"
change_setting "DNSMASQ_LISTENING" "bind"
else else
echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}" echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}"
change_setting "DNSMASQ_LISTENING" "single" change_setting "DNSMASQ_LISTENING" "single"
@ -673,18 +666,18 @@ addAudit()
domains="" domains=""
for domain in "$@" for domain in "$@"
do do
# Check domain to be added. Only continue if it is valid # Check domain to be added. Only continue if it is valid
validDomain="$(checkDomain "${domain}")" validDomain="$(checkDomain "${domain}")"
if [[ -n "${validDomain}" ]]; then if [[ -n "${validDomain}" ]]; then
# Put comma in between domains when there is # Put comma in between domains when there is
# more than one domains to be added # more than one domains to be added
# SQL INSERT allows adding multiple rows at once using the format # SQL INSERT allows adding multiple rows at once using the format
## INSERT INTO table (domain) VALUES ('abc.de'),('fgh.ij'),('klm.no'),('pqr.st'); ## INSERT INTO table (domain) VALUES ('abc.de'),('fgh.ij'),('klm.no'),('pqr.st');
if [[ -n "${domains}" ]]; then if [[ -n "${domains}" ]]; then
domains="${domains}," domains="${domains},"
fi
domains="${domains}('${domain}')"
fi fi
domains="${domains}('${domain}')"
fi
done done
# Insert only the domain here. The date_added field will be # Insert only the domain here. The date_added field will be
# filled with its default value (date_added = current timestamp) # filled with its default value (date_added = current timestamp)
@ -726,7 +719,7 @@ AddCustomDNSAddress() {
# Restart dnsmasq to load new custom DNS entries only if $reload not false # Restart dnsmasq to load new custom DNS entries only if $reload not false
if [[ ! $reload == "false" ]]; then if [[ ! $reload == "false" ]]; then
RestartDNS RestartDNS
fi fi
} }
@ -745,14 +738,14 @@ RemoveCustomDNSAddress() {
echo -e " ${CROSS} Invalid IP has been passed" echo -e " ${CROSS} Invalid IP has been passed"
exit 1 exit 1
fi fi
else else
echo " ${CROSS} Invalid Domain passed!" echo " ${CROSS} Invalid Domain passed!"
exit 1 exit 1
fi fi
# Restart dnsmasq to load new custom DNS entries only if reload is not false # Restart dnsmasq to load new custom DNS entries only if reload is not false
if [[ ! $reload == "false" ]]; then if [[ ! $reload == "false" ]]; then
RestartDNS RestartDNS
fi fi
} }
@ -767,10 +760,10 @@ AddCustomCNAMERecord() {
if [[ -n "${validDomain}" ]]; then if [[ -n "${validDomain}" ]]; then
validTarget="$(checkDomain "${target}")" validTarget="$(checkDomain "${target}")"
if [[ -n "${validTarget}" ]]; then if [[ -n "${validTarget}" ]]; then
echo "cname=${validDomain},${validTarget}" >> "${dnscustomcnamefile}" echo "cname=${validDomain},${validTarget}" >> "${dnscustomcnamefile}"
else else
echo " ${CROSS} Invalid Target Passed!" echo " ${CROSS} Invalid Target Passed!"
exit 1 exit 1
fi fi
else else
echo " ${CROSS} Invalid Domain passed!" echo " ${CROSS} Invalid Domain passed!"
@ -778,7 +771,7 @@ AddCustomCNAMERecord() {
fi fi
# Restart dnsmasq to load new custom CNAME records only if reload is not false # Restart dnsmasq to load new custom CNAME records only if reload is not false
if [[ ! $reload == "false" ]]; then if [[ ! $reload == "false" ]]; then
RestartDNS RestartDNS
fi fi
} }
@ -795,8 +788,8 @@ RemoveCustomCNAMERecord() {
if [[ -n "${validTarget}" ]]; then if [[ -n "${validTarget}" ]]; then
sed -i "/cname=${validDomain},${validTarget}$/d" "${dnscustomcnamefile}" sed -i "/cname=${validDomain},${validTarget}$/d" "${dnscustomcnamefile}"
else else
echo " ${CROSS} Invalid Target Passed!" echo " ${CROSS} Invalid Target Passed!"
exit 1 exit 1
fi fi
else else
echo " ${CROSS} Invalid Domain passed!" echo " ${CROSS} Invalid Domain passed!"
@ -805,7 +798,7 @@ RemoveCustomCNAMERecord() {
# Restart dnsmasq to update removed custom CNAME records only if $reload not false # Restart dnsmasq to update removed custom CNAME records only if $reload not false
if [[ ! $reload == "false" ]]; then if [[ ! $reload == "false" ]]; then
RestartDNS RestartDNS
fi fi
} }
@ -829,8 +822,6 @@ main() {
"layout" ) SetWebUILayout;; "layout" ) SetWebUILayout;;
"theme" ) SetWebUITheme;; "theme" ) SetWebUITheme;;
"-h" | "--help" ) helpFunc;; "-h" | "--help" ) helpFunc;;
"privacymode" ) SetPrivacyMode;;
"resolve" ) ResolutionSettings;;
"addstaticdhcp" ) AddDHCPStaticAddress;; "addstaticdhcp" ) AddDHCPStaticAddress;;
"removestaticdhcp" ) RemoveDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;;
"-e" | "email" ) SetAdminEmail "$3";; "-e" | "email" ) SetAdminEmail "$3";;

@ -85,8 +85,8 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("") url.access-deny = ("")
} }
# allow teleporter iframe on settings page # allow teleporter and API qr code iframe on settings page
$HTTP["url"] =~ "/teleporter\.php$" { $HTTP["url"] =~ "/(teleporter|api_token)\.php$" {
$HTTP["referer"] =~ "/admin/settings\.php" { $HTTP["referer"] =~ "/admin/settings\.php" {
setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" )
} }

@ -93,8 +93,8 @@ $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("") url.access-deny = ("")
} }
# allow teleporter iframe on settings page # allow teleporter and API qr code iframe on settings page
$HTTP["url"] =~ "/teleporter\.php$" { $HTTP["url"] =~ "/(teleporter|api_token)\.php$" {
$HTTP["referer"] =~ "/admin/settings\.php" { $HTTP["referer"] =~ "/admin/settings\.php" {
setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" ) setenv.add-response-header = ( "X-Frame-Options" => "SAMEORIGIN" )
} }

@ -2,7 +2,7 @@
# shellcheck disable=SC1090 # shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2017-2018 Pi-hole, LLC (https://pi-hole.net) # (c) 2017-2021 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
# #
# Installs and Updates Pi-hole # Installs and Updates Pi-hole
@ -76,7 +76,7 @@ PI_HOLE_CONFIG_DIR="/etc/pihole"
PI_HOLE_BIN_DIR="/usr/local/bin" PI_HOLE_BIN_DIR="/usr/local/bin"
PI_HOLE_BLOCKPAGE_DIR="${webroot}/pihole" PI_HOLE_BLOCKPAGE_DIR="${webroot}/pihole"
if [ -z "$useUpdateVars" ]; then if [ -z "$useUpdateVars" ]; then
useUpdateVars=false useUpdateVars=false
fi fi
adlistFile="/etc/pihole/adlists.list" adlistFile="/etc/pihole/adlists.list"
@ -90,7 +90,7 @@ PRIVACY_LEVEL=0
CACHE_SIZE=10000 CACHE_SIZE=10000
if [ -z "${USER}" ]; then if [ -z "${USER}" ]; then
USER="$(id -un)" USER="$(id -un)"
fi fi
# whiptail dialog dimensions: 20 rows and 70 chars width assures to fit on small screens and is known to hold all content. # whiptail dialog dimensions: 20 rows and 70 chars width assures to fit on small screens and is known to hold all content.
@ -133,7 +133,7 @@ fi
# A simple function that just echoes out our logo in ASCII format # A simple function that just echoes out our logo in ASCII format
# This lets users know that it is a Pi-hole, LLC product # This lets users know that it is a Pi-hole, LLC product
show_ascii_berry() { show_ascii_berry() {
echo -e " echo -e "
${COL_LIGHT_GREEN}.;;,. ${COL_LIGHT_GREEN}.;;,.
.ccccc:,. .ccccc:,.
:cccclll:. ..,, :cccclll:. ..,,
@ -172,7 +172,7 @@ os_check() {
local remote_os_domain valid_os valid_version valid_response detected_os detected_version display_warning cmdResult digReturnCode response local remote_os_domain valid_os valid_version valid_response detected_os detected_version display_warning cmdResult digReturnCode response
remote_os_domain=${OS_CHECK_DOMAIN_NAME:-"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_os=$(grep '^ID=' /etc/os-release | cut -d '=' -f2 | tr -d '"')
detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"') detected_version=$(grep VERSION_ID /etc/os-release | cut -d '=' -f2 | tr -d '"')
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 $?)"
@ -261,174 +261,174 @@ os_check() {
# Compatibility # Compatibility
package_manager_detect() { package_manager_detect() {
# First check to see if apt-get is installed. # First check to see if apt-get is installed.
if is_command apt-get ; then if is_command apt-get ; then
# Set some global variables here # Set some global variables here
# We don't set them earlier since the installed package manager might be rpm, so these values would be different # We don't set them earlier since the installed package manager might be rpm, so these values would be different
PKG_MANAGER="apt-get" PKG_MANAGER="apt-get"
# A variable to store the command used to update the package cache # A variable to store the command used to update the package cache
UPDATE_PKG_CACHE="${PKG_MANAGER} update" UPDATE_PKG_CACHE="${PKG_MANAGER} update"
# The command we will use to actually install packages # The command we will use to actually install packages
PKG_INSTALL=("${PKG_MANAGER}" -qq --no-install-recommends install) PKG_INSTALL=("${PKG_MANAGER}" -qq --no-install-recommends install)
# 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. # 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" PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true"
# Update package cache # Update package cache
update_package_cache || exit 1 update_package_cache || exit 1
# Check for and determine version number (major and minor) of current php install # Check for and determine version number (major and minor) of current php install
local phpVer="php" local phpVer="php"
if is_command php ; then if is_command php ; then
printf " %b Existing PHP installation detected : PHP version %s\\n" "${INFO}" "$(php <<< "<?php echo PHP_VERSION ?>")" printf " %b Existing PHP installation detected : PHP version %s\\n" "${INFO}" "$(php <<< "<?php echo PHP_VERSION ?>")"
printf -v phpInsMajor "%d" "$(php <<< "<?php echo PHP_MAJOR_VERSION ?>")" printf -v phpInsMajor "%d" "$(php <<< "<?php echo PHP_MAJOR_VERSION ?>")"
printf -v phpInsMinor "%d" "$(php <<< "<?php echo PHP_MINOR_VERSION ?>")" printf -v phpInsMinor "%d" "$(php <<< "<?php echo PHP_MINOR_VERSION ?>")"
phpVer="php$phpInsMajor.$phpInsMinor" phpVer="php$phpInsMajor.$phpInsMinor"
fi fi
# Packages required to perfom the os_check (stored as an array) # Packages required to perfom the os_check (stored as an array)
OS_CHECK_DEPS=(grep dnsutils) OS_CHECK_DEPS=(grep dnsutils)
# Packages required to run this install script (stored as an array) # Packages required to run this install script (stored as an array)
INSTALLER_DEPS=(git iproute2 whiptail ca-certificates) INSTALLER_DEPS=(git iproute2 whiptail ca-certificates)
# Packages required to run Pi-hole (stored as an array) # 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) 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) # 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 # 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") 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 # 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 if [[ -z "${phpInsMajor}" || "${phpInsMajor}" -lt 8 ]]; then
PIHOLE_WEB_DEPS+=("${phpVer}-json") PIHOLE_WEB_DEPS+=("${phpVer}-json")
fi fi
# The Web server user, # The Web server user,
LIGHTTPD_USER="www-data" LIGHTTPD_USER="www-data"
# group, # group,
LIGHTTPD_GROUP="www-data" LIGHTTPD_GROUP="www-data"
# and config file # and config file
LIGHTTPD_CFG="lighttpd.conf.debian" LIGHTTPD_CFG="lighttpd.conf.debian"
# This function waits for dpkg to unlock, which signals that the previous apt-get command has finished. # This function waits for dpkg to unlock, which signals that the previous apt-get command has finished.
test_dpkg_lock() { test_dpkg_lock() {
i=0 i=0
# fuser is a program to show which processes use the named files, sockets, or filesystems # fuser is a program to show which processes use the named files, sockets, or filesystems
# So while the lock is held, # So while the lock is held,
while fuser /var/lib/dpkg/lock >/dev/null 2>&1 while fuser /var/lib/dpkg/lock >/dev/null 2>&1
do do
# we wait half a second, # we wait half a second,
sleep 0.5 sleep 0.5
# increase the iterator, # increase the iterator,
((i=i+1)) ((i=i+1))
done done
# and then report success once dpkg is unlocked. # and then report success once dpkg is unlocked.
return 0 return 0
} }
# If apt-get is not found, check for rpm. # If apt-get is not found, check for rpm.
elif is_command rpm ; then elif is_command rpm ; then
# Then check if dnf or yum is the package manager # Then check if dnf or yum is the package manager
if is_command dnf ; then if is_command dnf ; then
PKG_MANAGER="dnf" PKG_MANAGER="dnf"
else
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"
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 neither apt-get or yum/dnf package managers were found
else else
PKG_MANAGER="yum" # we cannot install required packages
printf " %b No supported package manager found\\n" "${CROSS}"
# so exit the installer
exit
fi 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"
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 neither apt-get or yum/dnf package managers were found
else
# we cannot install required packages
printf " %b No supported package manager found\\n" "${CROSS}"
# so exit the installer
exit
fi
} }
select_rpm_php(){ select_rpm_php(){
# If the host OS is Fedora, # If the host OS is Fedora,
if grep -qiE 'fedora|fedberry' /etc/redhat-release; then if grep -qiE 'fedora|fedberry' /etc/redhat-release; then
# all required packages should be available by default with the latest fedora release # all required packages should be available by default with the latest fedora release
: # continue : # continue
# or if host OS is CentOS, # or if host OS is CentOS,
elif grep -qiE 'centos|scientific' /etc/redhat-release; then elif grep -qiE 'centos|scientific' /etc/redhat-release; then
# Pi-Hole currently supports CentOS 7+ with PHP7+ # Pi-Hole currently supports CentOS 7+ with PHP7+
SUPPORTED_CENTOS_VERSION=7 SUPPORTED_CENTOS_VERSION=7
SUPPORTED_CENTOS_PHP_VERSION=7 SUPPORTED_CENTOS_PHP_VERSION=7
# Check current CentOS major release version # Check current CentOS major release version
CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.?)' /etc/redhat-release) CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.?)' /etc/redhat-release)
# Check if CentOS version is supported # Check if CentOS version is supported
if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then
printf " %b CentOS %s is not supported.\\n" "${CROSS}" "${CURRENT_CENTOS_VERSION}" 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}" printf " Please update to CentOS release %s or later.\\n" "${SUPPORTED_CENTOS_VERSION}"
# exit the installer # exit the installer
exit exit
fi fi
# php-json is not required on CentOS 7 as it is already compiled into php # php-json is not required on CentOS 7 as it is already compiled into php
# verifiy via `php -m | grep json` # verifiy via `php -m | grep json`
if [[ $CURRENT_CENTOS_VERSION -eq 7 ]]; then if [[ $CURRENT_CENTOS_VERSION -eq 7 ]]; then
# create a temporary array as arrays are not designed for use as mutable data structures # create a temporary array as arrays are not designed for use as mutable data structures
CENTOS7_PIHOLE_WEB_DEPS=() CENTOS7_PIHOLE_WEB_DEPS=()
for i in "${!PIHOLE_WEB_DEPS[@]}"; do for i in "${!PIHOLE_WEB_DEPS[@]}"; do
if [[ ${PIHOLE_WEB_DEPS[i]} != "php-json" ]]; then if [[ ${PIHOLE_WEB_DEPS[i]} != "php-json" ]]; then
CENTOS7_PIHOLE_WEB_DEPS+=( "${PIHOLE_WEB_DEPS[i]}" ) CENTOS7_PIHOLE_WEB_DEPS+=( "${PIHOLE_WEB_DEPS[i]}" )
fi fi
done done
# re-assign the clean dependency array back to PIHOLE_WEB_DEPS # re-assign the clean dependency array back to PIHOLE_WEB_DEPS
PIHOLE_WEB_DEPS=("${CENTOS7_PIHOLE_WEB_DEPS[@]}") PIHOLE_WEB_DEPS=("${CENTOS7_PIHOLE_WEB_DEPS[@]}")
unset CENTOS7_PIHOLE_WEB_DEPS unset CENTOS7_PIHOLE_WEB_DEPS
fi fi
# CentOS requires the EPEL repository to gain access to Fedora packages # CentOS requires the EPEL repository to gain access to Fedora packages
EPEL_PKG="epel-release" EPEL_PKG="epel-release"
rpm -q ${EPEL_PKG} &> /dev/null || rc=$? rpm -q ${EPEL_PKG} &> /dev/null || rc=$?
if [[ $rc -ne 0 ]]; then if [[ $rc -ne 0 ]]; then
printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}" printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}"
"${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null
printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}" printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}"
fi fi
# The default php on CentOS 7.x is 5.4 which is EOL # 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 # Check if the version of PHP available via installed repositories is >= to PHP 7
AVAILABLE_PHP_VERSION=$("${PKG_MANAGER}" info php | grep -i version | grep -o '[0-9]\+' | head -1) AVAILABLE_PHP_VERSION=$("${PKG_MANAGER}" info php | grep -i version | grep -o '[0-9]\+' | head -1)
if [[ $AVAILABLE_PHP_VERSION -ge $SUPPORTED_CENTOS_PHP_VERSION ]]; then if [[ $AVAILABLE_PHP_VERSION -ge $SUPPORTED_CENTOS_PHP_VERSION ]]; then
# Since PHP 7 is available by default, install via default PHP package names # Since PHP 7 is available by default, install via default PHP package names
: # do nothing as PHP is current : # 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 else
printf " %b Enabling Remi's RPM repository (https://rpms.remirepo.net)\\n" "${INFO}" REMI_PKG="remi-release"
"${PKG_INSTALL[@]}" "https://rpms.remirepo.net/enterprise/${REMI_PKG}-$(rpm -E '%{rhel}').rpm" &> /dev/null REMI_REPO="remi-php72"
# enable the PHP 7 repository via yum-config-manager (provided by yum-utils) rpm -q ${REMI_PKG} &> /dev/null || rc=$?
"${PKG_INSTALL[@]}" "yum-utils" &> /dev/null if [[ $rc -ne 0 ]]; then
yum-config-manager --enable ${REMI_REPO} &> /dev/null # The PHP version available via default repositories is older than version 7
printf " %b Remi's RPM repository has been enabled for PHP7\\n" "${TICK}" 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
# trigger an install/update of PHP to ensure previous version of PHP is updated from REMI # User decided to NOT update PHP from REMI, attempt to install the default available PHP version
if "${PKG_INSTALL[@]}" "php-cli" &> /dev/null; then printf " %b User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use.\\n" "${INFO}"
printf " %b PHP7 installed/updated via Remi's RPM repository\\n" "${TICK}" : # 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 else
printf " %b There was a problem updating to PHP7 via Remi's RPM repository\\n" "${CROSS}" printf " %b Continuing installation with unsupported RPM based distribution\\n" "${INFO}"
exit 1
fi fi
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
fi
} }
# A function for checking if a directory is a git repository # A function for checking if a directory is a git repository
@ -519,7 +519,7 @@ update_repo() {
# In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks) # In case extra commits have been added after tagging/release (i.e in case of metadata updates/README.MD tweaks)
curBranch=$(git rev-parse --abbrev-ref HEAD) curBranch=$(git rev-parse --abbrev-ref HEAD)
if [[ "${curBranch}" == "master" ]]; then if [[ "${curBranch}" == "master" ]]; then
git reset --hard "$(git describe --abbrev=0 --tags)" || return $? git reset --hard "$(git describe --abbrev=0 --tags)" || return $?
fi fi
# Show a completion message # Show a completion message
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
@ -629,12 +629,12 @@ welcomeDialogs() {
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. 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 Choose yes to indicate that you have understood this message, and wish to continue" "${r}" "${c}"; then
#Nothing to do, continue #Nothing to do, continue
echo echo
else else
printf " %b Installer exited at static IP message.\\n" "${INFO}" printf " %b Installer exited at static IP message.\\n" "${INFO}"
exit 1 exit 1
fi fi
} }
# A function that lets the user pick an interface to use with Pi-hole # A function that lets the user pick an interface to use with Pi-hole
@ -674,7 +674,7 @@ chooseInterface() {
# Feed the available interfaces into this while loop # Feed the available interfaces into this while loop
done <<< "${availableInterfaces}" done <<< "${availableInterfaces}"
# The whiptail command that will be run, stored in a variable # The whiptail command that will be run, stored in a variable
chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to toggle selection)" "${r}" "${c}" "${interfaceCount}") chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to toggle selection)" "${r}" "${c}" 6)
# Now run the command using the interfaces saved into the array # Now run the command using the interfaces saved into the array
chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \ chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \
# If the user chooses Cancel, exit # If the user chooses Cancel, exit
@ -759,9 +759,8 @@ collect_v4andv6_information() {
printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}"
# if `dhcpcd` is used offer to set this as static IP for the device # if `dhcpcd` is used offer to set this as static IP for the device
if [[ -f "/etc/dhcpcd.conf" ]]; then if [[ -f "/etc/dhcpcd.conf" ]]; then
# configure networking via dhcpcd # configure networking via dhcpcd
getStaticIPv4Settings getStaticIPv4Settings
setDHCPCD
fi fi
find_IPv6_information find_IPv6_information
printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}" printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}"
@ -770,47 +769,59 @@ collect_v4andv6_information() {
getStaticIPv4Settings() { getStaticIPv4Settings() {
# Local, named variables # Local, named variables
local ipSettingsCorrect local ipSettingsCorrect
local DHCPChoice
# Ask if the user wants to use DHCP settings as their static IP # Ask if the user wants to use DHCP settings as their static IP
# This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions # This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address? DHCPChoice=$(whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --menu --separate-output "Do you want to use your current network settings as a static address? \\n
IP address: ${IPV4_ADDRESS} IP address: ${IPV4_ADDRESS} \\n
Gateway: ${IPv4gw}" "${r}" "${c}"; then Gateway: ${IPv4gw} \\n" "${r}" "${c}" 3\
"Yes" "Set static IP using current values" \
"No" "Set static IP using custom values" \
"Skip" "I will set a static IP later, or have already done so" 3>&2 2>&1 1>&3) || \
{ printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; }
case ${DHCPChoice} in
"Yes")
# If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict. # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict.
whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that.
If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want.
It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." "${r}" "${c}" It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." "${r}" "${c}"
# Nothing else to do since the variables are already set above # Nothing else to do since the variables are already set above
else setDHCPCD
# Otherwise, we need to ask the user to input their desired settings. ;;
# Start by getting the IPv4 address (pre-filling it with info gathered from DHCP)
# Start a loop to let the user enter their information with the chance to go back and edit it if necessary "No")
until [[ "${ipSettingsCorrect}" = True ]]; do # Otherwise, we need to ask the user to input their desired settings.
# Start by getting the IPv4 address (pre-filling it with info gathered from DHCP)
# Ask for the IPv4 address # Start a loop to let the user enter their information with the chance to go back and edit it if necessary
IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" "${r}" "${c}" "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \ until [[ "${ipSettingsCorrect}" = True ]]; do
# Canceling IPv4 settings window
{ ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } # Ask for the IPv4 address
printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" "${r}" "${c}" "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \
# Canceling IPv4 settings window
# Ask for the gateway { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" "${r}" "${c}" "${IPv4gw}" 3>&1 1>&2 2>&3) || \ printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}"
# Canceling gateway settings window
{ ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } # Ask for the gateway
printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}" IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" "${r}" "${c}" "${IPv4gw}" 3>&1 1>&2 2>&3) || \
# Canceling gateway settings window
# Give the user a chance to review their settings before moving on { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}"
IP address: ${IPV4_ADDRESS}
Gateway: ${IPv4gw}" "${r}" "${c}"; then # Give the user a chance to review their settings before moving on
# After that's done, the loop ends and we move on if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct?
ipSettingsCorrect=True IP address: ${IPV4_ADDRESS}
else Gateway: ${IPv4gw}" "${r}" "${c}"; then
# If the settings are wrong, the loop continues # After that's done, the loop ends and we move on
ipSettingsCorrect=False ipSettingsCorrect=True
fi else
done # If the settings are wrong, the loop continues
# End the if statement for DHCP vs. static ipSettingsCorrect=False
fi fi
done
setDHCPCD
;;
esac
} }
# Configure networking via dhcpcd # Configure networking via dhcpcd
@ -845,7 +856,7 @@ valid_ip() {
# Regex matching an optional port (starting with '#') range of 1-65536 # Regex matching an optional port (starting with '#') range of 1-65536
local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?"; local portelem="(#(6553[0-5]|655[0-2][0-9]|65[0-4][0-9]{2}|6[0-4][0-9]{3}|[1-5][0-9]{4}|[1-9][0-9]{0,3}|0))?";
# Build a full IPv4 regex from the above subexpressions # Build a full IPv4 regex from the above subexpressions
local regex="^${ipv4elem}\.${ipv4elem}\.${ipv4elem}\.${ipv4elem}${portelem}$" local regex="^${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}${portelem}$"
# Evaluate the regex, and return the result # Evaluate the regex, and return the result
[[ $ip =~ ${regex} ]] [[ $ip =~ ${regex} ]]
@ -902,8 +913,8 @@ setDNS() {
IFS=${OIFS} IFS=${OIFS}
# In a whiptail dialog, show the options # In a whiptail dialog, show the options
DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." "${r}" "${c}" 7 \ DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." "${r}" "${c}" 7 \
"${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \ "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \
# Exit if the user selects "Cancel" # Exit if the user selects "Cancel"
{ printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } { 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
@ -1169,8 +1180,8 @@ version_check_dnsmasq() {
install -D -m 644 -T "${dnsmasq_original_config}" "${dnsmasq_conf}" install -D -m 644 -T "${dnsmasq_original_config}" "${dnsmasq_conf}"
printf "%b %b Restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}" printf "%b %b Restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}"
else else
# Otherwise, don't to anything # Otherwise, don't to anything
printf " it is not a Pi-hole file, leaving alone!\\n" printf " it is not a Pi-hole file, leaving alone!\\n"
fi fi
else else
# If a file cannot be found, # If a file cannot be found,
@ -1205,8 +1216,8 @@ version_check_dnsmasq() {
sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_target}" sed -i '/^server=@DNS2@/d' "${dnsmasq_pihole_01_target}"
fi fi
# Set the cache size # Set the cache size
sed -i "s/@CACHE_SIZE@/$CACHE_SIZE/" "${dnsmasq_pihole_01_target}" 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}" sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' "${dnsmasq_conf}"
@ -1481,8 +1492,14 @@ update_package_cache() {
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
# Otherwise, show an error and exit # Otherwise, show an error and exit
# In case we used apt-get and apt is also available, we use this as recommendation as we have seen it
# gives more user-friendly (interactive) advice
if [[ ${PKG_MANAGER} == "apt-get" ]] && is_command apt ; then
UPDATE_PKG_CACHE="apt update"
fi
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %bError: Unable to update package cache. Please try \"%s\"%b" "${COL_LIGHT_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}" printf " %bError: Unable to update package cache. Please try \"%s\"%b\\n" "${COL_LIGHT_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}"
return 1 return 1
fi fi
} }
@ -1548,7 +1565,7 @@ install_dependent_packages() {
# Install Fedora/CentOS packages # Install Fedora/CentOS packages
for i in "$@"; do for i in "$@"; do
# For each package, check if it's already installed (and if so, don't add it to the installArray) # For each package, check if it's already installed (and if so, don't add it to the installArray)
printf " %b Checking for %s..." "${INFO}" "${i}" printf " %b Checking for %s..." "${INFO}" "${i}"
if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then if "${PKG_MANAGER}" -q list installed "${i}" &> /dev/null; then
printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}"
@ -1718,18 +1735,18 @@ finalExports() {
fi fi
# echo the information to the user # echo the information to the user
{ {
echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}" echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}"
echo "IPV4_ADDRESS=${IPV4_ADDRESS}" echo "IPV4_ADDRESS=${IPV4_ADDRESS}"
echo "IPV6_ADDRESS=${IPV6_ADDRESS}" echo "IPV6_ADDRESS=${IPV6_ADDRESS}"
echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}" echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}"
echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}"
echo "QUERY_LOGGING=${QUERY_LOGGING}" echo "QUERY_LOGGING=${QUERY_LOGGING}"
echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}" echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}"
echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}" echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}"
echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}"
echo "CACHE_SIZE=${CACHE_SIZE}" echo "CACHE_SIZE=${CACHE_SIZE}"
echo "DNS_FQDN_REQUIRED=${DNS_FQDN_REQUIRED:-true}" echo "DNS_FQDN_REQUIRED=${DNS_FQDN_REQUIRED:-true}"
echo "DNS_BOGUS_PRIV=${DNS_BOGUS_PRIV:-true}" echo "DNS_BOGUS_PRIV=${DNS_BOGUS_PRIV:-true}"
}>> "${setupVars}" }>> "${setupVars}"
chmod 644 "${setupVars}" chmod 644 "${setupVars}"
@ -1845,7 +1862,7 @@ checkSelinux() {
local CURRENT_SELINUX local CURRENT_SELINUX
local SELINUX_ENFORCING=0 local SELINUX_ENFORCING=0
# Check for SELinux configuration file and getenforce command # Check for SELinux configuration file and getenforce command
if [[ -f /etc/selinux/config ]] && command -v getenforce &> /dev/null; then if [[ -f /etc/selinux/config ]] && is_command getenforce; then
# Check the default SELinux mode # Check the default SELinux mode
DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config) DEFAULT_SELINUX=$(awk -F= '/^SELINUX=/ {print $2}' /etc/selinux/config)
case "${DEFAULT_SELINUX,,}" in case "${DEFAULT_SELINUX,,}" in
@ -1904,7 +1921,7 @@ displayFinalMessage() {
additional="View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin additional="View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin
Your Admin Webpage login password is ${pwstring}" Your Admin Webpage login password is ${pwstring}"
fi fi
# Final completion message to user # Final completion message to user
whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using: whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using:
@ -2077,7 +2094,6 @@ clone_or_update_repos() {
# shellcheck disable=SC2120 # shellcheck disable=SC2120
FTLinstall() { FTLinstall() {
# Local, named variables # Local, named variables
local latesttag
local str="Downloading and Installing FTL" local str="Downloading and Installing FTL"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
@ -2148,7 +2164,7 @@ FTLinstall() {
disable_dnsmasq() { disable_dnsmasq() {
# dnsmasq can now be stopped and disabled if it exists # dnsmasq can now be stopped and disabled if it exists
if which dnsmasq &> /dev/null; then if is_command dnsmasq; then
if check_service_active "dnsmasq";then if check_service_active "dnsmasq";then
printf " %b FTL can now resolve DNS Queries without dnsmasq running separately\\n" "${INFO}" printf " %b FTL can now resolve DNS Queries without dnsmasq running separately\\n" "${INFO}"
stop_service dnsmasq stop_service dnsmasq
@ -2278,7 +2294,7 @@ FTLcheckUpdate() {
local localSha1 local localSha1
# if dnsmasq exists and is running at this point, force reinstall of FTL Binary # if dnsmasq exists and is running at this point, force reinstall of FTL Binary
if which dnsmasq &> /dev/null; then if is_command dnsmasq; then
if check_service_active "dnsmasq";then if check_service_active "dnsmasq";then
return 0 return 0
fi fi
@ -2439,7 +2455,7 @@ main() {
#In case of RPM based distro, select the proper PHP version #In case of RPM based distro, select the proper PHP version
if [[ "$PKG_MANAGER" == "yum" || "$PKG_MANAGER" == "dnf" ]] ; then if [[ "$PKG_MANAGER" == "yum" || "$PKG_MANAGER" == "dnf" ]] ; then
select_rpm_php select_rpm_php
fi fi
# Check if SELinux is Enforcing # Check if SELinux is Enforcing
@ -2469,12 +2485,12 @@ main() {
get_available_interfaces get_available_interfaces
# Find interfaces and let the user choose one # Find interfaces and let the user choose one
chooseInterface chooseInterface
# find IPv4 and IPv6 information of the device
collect_v4andv6_information
# Decide what upstream DNS Servers to use # Decide what upstream DNS Servers to use
setDNS setDNS
# Give the user a choice of blocklists to include in their install. Or not. # Give the user a choice of blocklists to include in their install. Or not.
chooseBlocklists chooseBlocklists
# 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 # Let the user decide if they want the web interface to be installed automatically
setAdminFlag setAdminFlag
# Let the user decide if they want query logging enabled... # Let the user decide if they want query logging enabled...

@ -75,7 +75,12 @@ fi
# Generate new sqlite3 file from schema template # Generate new sqlite3 file from schema template
generate_gravity_database() { generate_gravity_database() {
sqlite3 "${1}" < "${gravityDBschema}" if ! sqlite3 "${gravityDBfile}" < "${gravityDBschema}"; then
echo -e " ${CROSS} Unable to create ${gravityDBfile}"
return 1
fi
chown pihole:pihole "${gravityDBfile}"
chmod g+w "${piholeDir}" "${gravityDBfile}"
} }
# Copy data from old to new database file and swap them # Copy data from old to new database file and swap them
@ -213,7 +218,7 @@ database_table_from_file() {
# Move source file to backup directory, create directory if not existing # Move source file to backup directory, create directory if not existing
mkdir -p "${backup_path}" mkdir -p "${backup_path}"
mv "${source}" "${backup_file}" 2> /dev/null || \ mv "${source}" "${backup_file}" 2> /dev/null || \
echo -e " ${CROSS} Unable to backup ${source} to ${backup_path}" echo -e " ${CROSS} Unable to backup ${source} to ${backup_path}"
# Delete tmpFile # Delete tmpFile
rm "${tmpFile}" > /dev/null 2>&1 || \ rm "${tmpFile}" > /dev/null 2>&1 || \
@ -248,7 +253,7 @@ database_adlist_number() {
return; return;
fi 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 ) output=$( { printf ".timeout 30000\\nUPDATE adlist SET number = %i, invalid_domains = %i WHERE id = %i;\\n" "${num_source_lines}" "${num_invalid}" "${1}" | sqlite3 "${gravityDBfile}"; } 2>&1 )
status="$?" status="$?"
if [[ "${status}" -ne 0 ]]; then if [[ "${status}" -ne 0 ]]; then
@ -279,7 +284,10 @@ migrate_to_database() {
if [ ! -e "${gravityDBfile}" ]; then if [ ! -e "${gravityDBfile}" ]; then
# Create new database file - note that this will be created in version 1 # Create new database file - note that this will be created in version 1
echo -e " ${INFO} Creating new gravity database" echo -e " ${INFO} Creating new gravity database"
generate_gravity_database "${gravityDBfile}" if ! generate_gravity_database; then
echo -e " ${CROSS} Error creating new gravity database. Please contact support."
return 1
fi
# Check if gravity database needs to be updated # Check if gravity database needs to be updated
upgrade_gravityDB "${gravityDBfile}" "${piholeDir}" upgrade_gravityDB "${gravityDBfile}" "${piholeDir}"
@ -430,9 +438,9 @@ gravity_DownloadBlocklists() {
compression="--compressed" compression="--compressed"
echo -e " ${INFO} Using libz compression\n" echo -e " ${INFO} Using libz compression\n"
else else
compression="" compression=""
echo -e " ${INFO} Libz compression not available\n" echo -e " ${INFO} Libz compression not available\n"
fi fi
# Loop through $sources and download each one # Loop through $sources and download each one
for ((i = 0; i < "${#sources[@]}"; i++)); do for ((i = 0; i < "${#sources[@]}"; i++)); do
url="${sources[$i]}" url="${sources[$i]}"
@ -462,9 +470,9 @@ gravity_DownloadBlocklists() {
check_url="$( sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<< "$url" )" check_url="$( sed -re 's#([^:/]*://)?([^/]+)@#\1\2#' <<< "$url" )"
if [[ "${check_url}" =~ ${regex} ]]; then if [[ "${check_url}" =~ ${regex} ]]; then
echo -e " ${CROSS} Invalid Target" echo -e " ${CROSS} Invalid Target"
else else
gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}" "${sourceIDs[$i]}" "${saveLocation}" "${target}" "${compression}" gravity_DownloadBlocklistFromUrl "${url}" "${cmd_ext}" "${agent}" "${sourceIDs[$i]}" "${saveLocation}" "${target}" "${compression}"
fi fi
echo "" echo ""
done done
@ -503,8 +511,9 @@ gravity_DownloadBlocklists() {
gravity_Blackbody=true gravity_Blackbody=true
} }
total_num=0 # num_target_lines does increase for every correctly added domain in pareseList()
num_lines=0 num_target_lines=0
num_source_lines=0
num_invalid=0 num_invalid=0
parseList() { parseList() {
local adlistID="${1}" src="${2}" target="${3}" incorrect_lines local adlistID="${1}" src="${2}" target="${3}" incorrect_lines
@ -516,18 +525,20 @@ parseList() {
# Find (up to) five domains containing invalid characters (see above) # Find (up to) five domains containing invalid characters (see above)
incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)" incorrect_lines="$(sed -e "/[^a-zA-Z0-9.\_-]/!d" "${src}" | head -n 5)"
local num_target_lines num_correct_lines num_invalid local num_target_lines_new num_correct_lines
# Get number of lines in source file # Get number of lines in source file
num_lines="$(grep -c "^" "${src}")" num_source_lines="$(grep -c "^" "${src}")"
# Get number of lines in destination file # Get the new number of lines in destination file
num_target_lines="$(grep -c "^" "${target}")" num_target_lines_new="$(grep -c "^" "${target}")"
num_correct_lines="$(( num_target_lines-total_num ))" # Number of new correctly added lines
total_num="$num_target_lines" num_correct_lines="$(( num_target_lines_new-num_target_lines ))"
num_invalid="$(( num_lines-num_correct_lines ))" # Upate number of lines in target file
num_target_lines="$num_target_lines_new"
num_invalid="$(( num_source_lines-num_correct_lines ))"
if [[ "${num_invalid}" -eq 0 ]]; then if [[ "${num_invalid}" -eq 0 ]]; then
echo " ${INFO} Analyzed ${num_lines} domains" echo " ${INFO} Analyzed ${num_source_lines} domains"
else else
echo " ${INFO} Analyzed ${num_lines} domains, ${num_invalid} domains invalid!" echo " ${INFO} Analyzed ${num_source_lines} domains, ${num_invalid} domains invalid!"
fi fi
# Display sample of invalid lines if we found some # Display sample of invalid lines if we found some
@ -583,28 +594,32 @@ gravity_DownloadBlocklistFromUrl() {
blocked=false blocked=false
case $BLOCKINGMODE in case $BLOCKINGMODE in
"IP-NODATA-AAAA"|"IP") "IP-NODATA-AAAA"|"IP")
# Get IP address of this domain # Get IP address of this domain
ip="$(dig "${domain}" +short)" ip="$(dig "${domain}" +short)"
# Check if this IP matches any IP of the system # Check if this IP matches any IP of the system
if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then if [[ -n "${ip}" && $(grep -Ec "inet(|6) ${ip}" <<< "$(ip a)") -gt 0 ]]; then
blocked=true blocked=true
fi;; fi;;
"NXDOMAIN") "NXDOMAIN")
if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then
blocked=true blocked=true
fi;; fi;;
"NODATA")
if [[ $(dig "${domain}" | grep "NOERROR" -c) -ge 1 ]] && [[ -z $(dig +short "${domain}") ]]; then
blocked=true
fi;;
"NULL"|*) "NULL"|*)
if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then
blocked=true blocked=true
fi;; fi;;
esac esac
if [[ "${blocked}" == true ]]; then if [[ "${blocked}" == true ]]; then
printf -v ip_addr "%s" "${PIHOLE_DNS_1%#*}" printf -v ip_addr "%s" "${PIHOLE_DNS_1%#*}"
if [[ ${PIHOLE_DNS_1} != *"#"* ]]; then if [[ ${PIHOLE_DNS_1} != *"#"* ]]; then
port=53 port=53
else else
printf -v port "%s" "${PIHOLE_DNS_1#*#}" printf -v port "%s" "${PIHOLE_DNS_1#*#}"
fi fi
ip=$(dig "@${ip_addr}" -p "${port}" +short "${domain}" | tail -1) ip=$(dig "@${ip_addr}" -p "${port}" +short "${domain}" | tail -1)
if [[ $(echo "${url}" | awk -F '://' '{print $1}') = "https" ]]; then if [[ $(echo "${url}" | awk -F '://' '{print $1}') = "https" ]]; then
@ -623,11 +638,11 @@ gravity_DownloadBlocklistFromUrl() {
case $url in case $url in
# Did we "download" a local file? # Did we "download" a local file?
"file"*) "file"*)
if [[ -s "${patternBuffer}" ]]; then if [[ -s "${patternBuffer}" ]]; then
echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true echo -e "${OVER} ${TICK} ${str} Retrieval successful"; success=true
else else
echo -e "${OVER} ${CROSS} ${str} Not found / empty list" echo -e "${OVER} ${CROSS} ${str} Not found / empty list"
fi;; fi;;
# Did we "download" a remote file? # Did we "download" a remote file?
*) *)
# Determine "Status:" output based on HTTP response # Determine "Status:" output based on HTTP response
@ -686,7 +701,7 @@ gravity_DownloadBlocklistFromUrl() {
else else
echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}" 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 # Manually reset these two numbers because we do not call parseList here
num_lines=0 num_source_lines=0
num_invalid=0 num_invalid=0
database_adlist_number "${adlistID}" database_adlist_number "${adlistID}"
database_adlist_status "${adlistID}" "4" database_adlist_status "${adlistID}" "4"
@ -845,6 +860,49 @@ gravity_Cleanup() {
fi fi
} }
database_recovery() {
local result
local str="Checking integrity of existing gravity database"
local option="${1}"
echo -ne " ${INFO} ${str}..."
if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA integrity_check" 2>&1)"; then
echo -e "${OVER} ${TICK} ${str} - no errors found"
str="Checking foreign keys of existing gravity database"
echo -ne " ${INFO} ${str}..."
if result="$(pihole-FTL sqlite3 "${gravityDBfile}" "PRAGMA foreign_key_check" 2>&1)"; then
echo -e "${OVER} ${TICK} ${str} - no errors found"
if [[ "${option}" != "force" ]]; then
return
fi
else
echo -e "${OVER} ${CROSS} ${str} - errors found:"
while IFS= read -r line ; do echo " - $line"; done <<< "$result"
fi
else
echo -e "${OVER} ${CROSS} ${str} - errors found:"
while IFS= read -r line ; do echo " - $line"; done <<< "$result"
fi
str="Trying to recover existing gravity database"
echo -ne " ${INFO} ${str}..."
# We have to remove any possibly existing recovery database or this will fail
rm -f "${gravityDBfile}.recovered" > /dev/null 2>&1
if result="$(pihole-FTL sqlite3 "${gravityDBfile}" ".recover" | pihole-FTL sqlite3 "${gravityDBfile}.recovered" 2>&1)"; then
echo -e "${OVER} ${TICK} ${str} - success"
mv "${gravityDBfile}" "${gravityDBfile}.old"
mv "${gravityDBfile}.recovered" "${gravityDBfile}"
echo -ne " ${INFO} ${gravityDBfile} has been recovered"
echo -ne " ${INFO} The old ${gravityDBfile} has been moved to ${gravityDBfile}.old"
else
echo -e "${OVER} ${CROSS} ${str} - the following errors happened:"
while IFS= read -r line ; do echo " - $line"; done <<< "$result"
echo -e " ${CROSS} Recovery failed. Try \"pihole -r recreate\" instead."
exit 1
fi
echo ""
}
helpFunc() { helpFunc() {
echo "Usage: pihole -g echo "Usage: pihole -g
Update domains from blocklists specified in adlists.list Update domains from blocklists specified in adlists.list
@ -855,10 +913,37 @@ Options:
exit 0 exit 0
} }
repairSelector() {
case "$1" in
"recover") recover_database=true;;
"recreate") recreate_database=true;;
*) echo "Usage: pihole -g -r {recover,recreate}
Attempt to repair gravity database
Available options:
pihole -g -r recover Try to recover a damaged gravity database file.
Pi-hole tries to restore as much as possible
from a corrupted gravity database.
pihole -g -r recover force Pi-hole will run the recovery process even when
no damage is detected. This option is meant to be
a last resort. Recovery is a fragile task
consuming a lot of resources and shouldn't be
performed unnecessarily.
pihole -g -r recreate Create a new gravity database file from scratch.
This will remove your existing gravity database
and create a new file from scratch. If you still
have the migration backup created when migrating
to Pi-hole v5.0, Pi-hole will import these files."
exit 0;;
esac
}
for var in "$@"; do for var in "$@"; do
case "${var}" in case "${var}" in
"-f" | "--force" ) forceDelete=true;; "-f" | "--force" ) forceDelete=true;;
"-r" | "--recreate" ) recreate_database=true;; "-r" | "--repair" ) repairSelector "$3";;
"-h" | "--help" ) helpFunc;; "-h" | "--help" ) helpFunc;;
esac esac
done done
@ -872,7 +957,7 @@ fi
gravity_Trap gravity_Trap
if [[ "${recreate_database:-}" == true ]]; then if [[ "${recreate_database:-}" == true ]]; then
str="Restoring from migration backup" str="Recreating gravity database from migration backup"
echo -ne "${INFO} ${str}..." echo -ne "${INFO} ${str}..."
rm "${gravityDBfile}" rm "${gravityDBfile}"
pushd "${piholeDir}" > /dev/null || exit pushd "${piholeDir}" > /dev/null || exit
@ -881,8 +966,15 @@ if [[ "${recreate_database:-}" == true ]]; then
echo -e "${OVER} ${TICK} ${str}" echo -e "${OVER} ${TICK} ${str}"
fi fi
if [[ "${recover_database:-}" == true ]]; then
database_recovery "$4"
fi
# Move possibly existing legacy files to the gravity database # Move possibly existing legacy files to the gravity database
migrate_to_database if ! migrate_to_database; then
echo -e " ${CROSS} Unable to migrate to database. Please contact support."
exit 1
fi
if [[ "${forceDelete:-}" == true ]]; then if [[ "${forceDelete:-}" == true ]]; then
str="Deleting existing list cache" str="Deleting existing list cache"
@ -893,14 +985,21 @@ if [[ "${forceDelete:-}" == true ]]; then
fi fi
# Gravity downloads blocklists next # Gravity downloads blocklists next
gravity_CheckDNSResolutionAvailable if ! gravity_CheckDNSResolutionAvailable; then
echo -e " ${CROSS} Can not complete gravity update, no DNS is available. Please contact support."
exit 1
fi
gravity_DownloadBlocklists gravity_DownloadBlocklists
# Create local.list # Create local.list
gravity_generateLocalList gravity_generateLocalList
# Migrate rest of the data from old to new database # Migrate rest of the data from old to new database
gravity_swap_databases if ! gravity_swap_databases; then
echo -e " ${CROSS} Unable to create database. Please contact support."
exit 1
fi
# Update gravity timestamp # Update gravity timestamp
update_gravity_timestamp update_gravity_timestamp

@ -71,8 +71,7 @@ reconfigurePiholeFunc() {
} }
updateGravityFunc() { updateGravityFunc() {
"${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@" exec "${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@"
exit $?
} }
queryFunc() { queryFunc() {
@ -95,8 +94,7 @@ uninstallFunc() {
versionFunc() { versionFunc() {
shift shift
"${PI_HOLE_SCRIPT_DIR}"/version.sh "$@" exec "${PI_HOLE_SCRIPT_DIR}"/version.sh "$@"
exit 0
} }
# Get PID of main pihole-FTL process # Get PID of main pihole-FTL process

@ -18,8 +18,8 @@ py.test -vv -n auto -m "build_stage"
py.test -vv -n auto -m "not build_stage" py.test -vv -n auto -m "not build_stage"
``` ```
The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change. The build_stage tests have to run first to create the docker images, followed by the actual tests which utilize said images. Unless you're changing your dockerfiles you shouldn't have to run the build_stage every time - but it's a good idea to rebuild at least once a day in case the base Docker images or packages change.
# How do I debug python? # How do I debug python?
Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :) Highly recommended: Setup PyCharm on a **Docker enabled** machine. Having a python debugger like PyCharm changes your life if you've never used it :)

@ -1,4 +1,5 @@
FROM centos:7 FROM centos:7
RUN yum install -y git
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole

@ -1,4 +1,5 @@
FROM centos:8 FROM centos:8
RUN yum install -y git
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole

@ -1,4 +1,5 @@
FROM fedora:33 FROM fedora:33
RUN dnf install -y git
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole

@ -1,4 +1,5 @@
FROM fedora:34 FROM fedora:34
RUN dnf install -y git
ENV GITDIR /etc/.pihole ENV GITDIR /etc/.pihole
ENV SCRIPTDIR /opt/pihole ENV SCRIPTDIR /opt/pihole

@ -1,10 +1,9 @@
import pytest import pytest
import testinfra import testinfra
import testinfra.backend.docker
import subprocess
from textwrap import dedent from textwrap import dedent
check_output = testinfra.get_backend(
"local://"
).get_module("Command").check_output
SETUPVARS = { SETUPVARS = {
'PIHOLE_INTERFACE': 'eth99', 'PIHOLE_INTERFACE': 'eth99',
@ -12,101 +11,120 @@ SETUPVARS = {
'PIHOLE_DNS_2': '4.2.2.2' 'PIHOLE_DNS_2': '4.2.2.2'
} }
IMAGE = 'pytest_pihole:test_container'
tick_box = "[\x1b[1;32m\u2713\x1b[0m]" tick_box = "[\x1b[1;32m\u2713\x1b[0m]"
cross_box = "[\x1b[1;31m\u2717\x1b[0m]" cross_box = "[\x1b[1;31m\u2717\x1b[0m]"
info_box = "[i]" info_box = "[i]"
@pytest.fixture # Monkeypatch sh to bash, if they ever support non hard code /bin/sh this can go away
def Pihole(Docker): # https://github.com/pytest-dev/pytest-testinfra/blob/master/testinfra/backend/docker.py
''' def run_bash(self, command, *args, **kwargs):
used to contain some script stubbing, now pretty much an alias. cmd = self.get_command(command, *args)
Also provides bash as the default run function shell if self.user is not None:
''' out = self.run_local(
def run_bash(self, command, *args, **kwargs): "docker exec -u %s %s /bin/bash -c %s", self.user, self.name, cmd
cmd = self.get_command(command, *args) )
if self.user is not None: else:
out = self.run_local( out = self.run_local("docker exec %s /bin/bash -c %s", self.name, cmd)
"docker exec -u %s %s /bin/bash -c %s", out.command = self.encode(cmd)
self.user, self.name, cmd) return out
else:
out = self.run_local(
"docker exec %s /bin/bash -c %s", self.name, cmd)
out.command = self.encode(cmd)
return out
funcType = type(Docker.run)
Docker.run = funcType(run_bash, Docker)
return Docker
@pytest.fixture
def Docker(request, args, image, cmd):
'''
combine our fixtures into a docker run command and setup finalizer to
cleanup
'''
assert 'docker' in check_output('id'), "Are you in the docker group?"
docker_run = "docker run {} {} {}".format(args, image, cmd)
docker_id = check_output(docker_run)
def teardown():
check_output("docker rm -f %s", docker_id)
request.addfinalizer(teardown)
docker_container = testinfra.get_backend("docker://" + docker_id) testinfra.backend.docker.DockerBackend.run = run_bash
docker_container.id = docker_id
return docker_container
@pytest.fixture @pytest.fixture
def args(request): def host():
''' # run a container
-t became required when tput began being used docker_id = subprocess.check_output(
''' ['docker', 'run', '-t', '-d', '--cap-add=ALL', IMAGE]).decode().strip()
return '-t -d'
# return a testinfra connection to the container
docker_host = testinfra.get_host("docker://" + docker_id)
@pytest.fixture(params=[ yield docker_host
'test_container' # at the end of the test suite, destroy the container
]) subprocess.check_call(['docker', 'rm', '-f', docker_id])
def tag(request):
'''
consumed by image to make the test matrix
'''
return request.param
@pytest.fixture() # Helper functions
def image(request, tag): def mock_command(script, args, container):
''' '''
built by test_000_build_containers.py Allows for setup of commands we don't really want to have to run for real
in unit tests
''' '''
return 'pytest_pihole:{}'.format(tag) full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent(r'''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script))
for k, v in args.items():
case = dedent('''
{arg})
echo {res}
exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1]))
mock_script += case
mock_script += dedent('''
esac''')
container.run('''
cat <<EOF> {script}\n{content}\nEOF
chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path,
content=mock_script,
scriptlog=script))
@pytest.fixture() def mock_command_passthrough(script, args, container):
def cmd(request):
''' '''
default to doing nothing by tailing null, but don't exit Per other mock_command* functions, allows intercepting of commands we don't want to run for real
in unit tests, however also allows only specific arguments to be mocked. Anything not defined will
be passed through to the actual command.
Example use-case: mocking `git pull` but still allowing `git clone` to work as intended
''' '''
return 'tail -f /dev/null' orig_script_path = container.check_output('command -v {}'.format(script))
full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent(r'''\
#!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script))
for k, v in args.items():
case = dedent('''
{arg})
echo {res}
exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1]))
mock_script += case
mock_script += dedent(r'''
*)
{orig_script_path} "\$@"
;;'''.format(orig_script_path=orig_script_path))
mock_script += dedent('''
esac''')
container.run('''
cat <<EOF> {script}\n{content}\nEOF
chmod +x {script}
rm -f /var/log/{scriptlog}'''.format(script=full_script_path,
content=mock_script,
scriptlog=script))
# Helper functions def mock_command_run(script, args, container):
def mock_command(script, args, container):
''' '''
Allows for setup of commands we don't really want to have to run for real Allows for setup of commands we don't really want to have to run for real
in unit tests in unit tests
''' '''
full_script_path = '/usr/local/bin/{}'.format(script) full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent('''\ mock_script = dedent(r'''\
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1" in'''.format(script=script)) case "\$1 \$2" in'''.format(script=script))
for k, v in args.items(): for k, v in args.items():
case = dedent(''' case = dedent('''
{arg}) \"{arg}\")
echo {res} echo {res}
exit {retcode} exit {retcode}
;;'''.format(arg=k, res=v[0], retcode=v[1])) ;;'''.format(arg=k, res=v[0], retcode=v[1]))
@ -127,7 +145,7 @@ def mock_command_2(script, args, container):
in unit tests in unit tests
''' '''
full_script_path = '/usr/local/bin/{}'.format(script) full_script_path = '/usr/local/bin/{}'.format(script)
mock_script = dedent('''\ mock_script = dedent(r'''\
#!/bin/bash -e #!/bin/bash -e
echo "\$0 \$@" >> /var/log/{script} echo "\$0 \$@" >> /var/log/{script}
case "\$1 \$2" in'''.format(script=script)) case "\$1 \$2" in'''.format(script=script))

@ -1,6 +1,6 @@
docker-compose==1.23.2 docker-compose
pytest==4.3.0 pytest
pytest-xdist==1.26.1 pytest-xdist
pytest-cov==2.6.1 pytest-cov
testinfra==1.19.0 pytest-testinfra
tox==3.7.0 tox

File diff suppressed because it is too large Load Diff

@ -5,11 +5,11 @@ from .conftest import (
) )
def test_php_upgrade_default_optout_centos_eq_7(Pihole): def test_php_upgrade_default_optout_centos_eq_7(host):
''' '''
confirms the default behavior to opt-out of installing PHP7 from REMI confirms the default behavior to opt-out of installing PHP7 from REMI
''' '''
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -17,18 +17,18 @@ def test_php_upgrade_default_optout_centos_eq_7(Pihole):
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
def test_php_upgrade_user_optout_centos_eq_7(Pihole): def test_php_upgrade_user_optout_centos_eq_7(host):
''' '''
confirms installer behavior when user opt-out of installing PHP7 from REMI confirms installer behavior when user opt-out of installing PHP7 from REMI
(php not currently installed) (php not currently installed)
''' '''
# Whiptail dialog returns Cancel for user prompt # Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole) mock_command('whiptail', {'*': ('', '1')}, host)
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -36,18 +36,18 @@ def test_php_upgrade_user_optout_centos_eq_7(Pihole):
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
def test_php_upgrade_user_optin_centos_eq_7(Pihole): def test_php_upgrade_user_optin_centos_eq_7(host):
''' '''
confirms installer behavior when user opt-in to installing PHP7 from REMI confirms installer behavior when user opt-in to installing PHP7 from REMI
(php not currently installed) (php not currently installed)
''' '''
# Whiptail dialog returns Continue for user prompt # Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, host)
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -59,5 +59,5 @@ def test_php_upgrade_user_optin_centos_eq_7(Pihole):
expected_stdout = tick_box + (' Remi\'s RPM repository has ' expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7') 'been enabled for PHP7')
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert remi_package.is_installed assert remi_package.is_installed

@ -5,12 +5,12 @@ from .conftest import (
) )
def test_php_upgrade_default_continue_centos_gte_8(Pihole): def test_php_upgrade_default_continue_centos_gte_8(host):
''' '''
confirms the latest version of CentOS continues / does not optout confirms the latest version of CentOS continues / does not optout
(should trigger on CentOS7 only) (should trigger on CentOS7 only)
''' '''
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -19,19 +19,19 @@ def test_php_upgrade_default_continue_centos_gte_8(Pihole):
' Deprecated PHP may be in use.') ' Deprecated PHP may be in use.')
assert unexpected_stdout not in package_manager_detect.stdout assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS # ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole): def test_php_upgrade_user_optout_skipped_centos_gte_8(host):
''' '''
confirms installer skips user opt-out of installing PHP7 from REMI on confirms installer skips user opt-out of installing PHP7 from REMI on
latest CentOS (should trigger on CentOS7 only) latest CentOS (should trigger on CentOS7 only)
(php not currently installed) (php not currently installed)
''' '''
# Whiptail dialog returns Cancel for user prompt # Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole) mock_command('whiptail', {'*': ('', '1')}, host)
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -40,19 +40,19 @@ def test_php_upgrade_user_optout_skipped_centos_gte_8(Pihole):
' Deprecated PHP may be in use.') ' Deprecated PHP may be in use.')
assert unexpected_stdout not in package_manager_detect.stdout assert unexpected_stdout not in package_manager_detect.stdout
# ensure remi was not installed on latest CentOS # ensure remi was not installed on latest CentOS
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole): def test_php_upgrade_user_optin_skipped_centos_gte_8(host):
''' '''
confirms installer skips user opt-in to installing PHP7 from REMI on confirms installer skips user opt-in to installing PHP7 from REMI on
latest CentOS (should trigger on CentOS7 only) latest CentOS (should trigger on CentOS7 only)
(php not currently installed) (php not currently installed)
''' '''
# Whiptail dialog returns Continue for user prompt # Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, host)
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -64,5 +64,5 @@ def test_php_upgrade_user_optin_skipped_centos_gte_8(Pihole):
unexpected_stdout = tick_box + (' Remi\'s RPM repository has ' unexpected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7') 'been enabled for PHP7')
assert unexpected_stdout not in package_manager_detect.stdout assert unexpected_stdout not in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed

@ -7,13 +7,13 @@ from .conftest import (
) )
def test_release_supported_version_check_centos(Pihole): def test_release_supported_version_check_centos(host):
''' '''
confirms installer exits on unsupported releases of CentOS confirms installer exits on unsupported releases of CentOS
''' '''
# modify /etc/redhat-release to mock an unsupported CentOS release # modify /etc/redhat-release to mock an unsupported CentOS release
Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release') host.run('echo "CentOS Linux release 6.9" > /etc/redhat-release')
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -24,11 +24,11 @@ def test_release_supported_version_check_centos(Pihole):
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
def test_enable_epel_repository_centos(Pihole): def test_enable_epel_repository_centos(host):
''' '''
confirms the EPEL package repository is enabled when installed on CentOS confirms the EPEL package repository is enabled when installed on CentOS
''' '''
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -38,22 +38,22 @@ def test_enable_epel_repository_centos(Pihole):
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
expected_stdout = tick_box + ' Installed epel-release' expected_stdout = tick_box + ' Installed epel-release'
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
epel_package = Pihole.package('epel-release') epel_package = host.package('epel-release')
assert epel_package.is_installed assert epel_package.is_installed
def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole): def test_php_version_lt_7_detected_upgrade_default_optout_centos(host):
''' '''
confirms the default behavior to opt-out of upgrading to PHP7 from REMI confirms the default behavior to opt-out of upgrading to PHP7 from REMI
''' '''
# first we will install the default php version to test installer behavior # first we will install the default php version to test installer behavior
php_install = Pihole.run('yum install -y php') php_install = host.run('yum install -y php')
assert php_install.rc == 0 assert php_install.rc == 0
php_package = Pihole.package('php') php_package = host.package('php')
default_centos_php_version = php_package.version.split('.')[0] default_centos_php_version = php_package.version.split('.')[0]
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
pytest.skip("Test deprecated . Detected default PHP version >= 7") pytest.skip("Test deprecated . Detected default PHP version >= 7")
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -61,24 +61,24 @@ def test_php_version_lt_7_detected_upgrade_default_optout_centos(Pihole):
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole): def test_php_version_lt_7_detected_upgrade_user_optout_centos(host):
''' '''
confirms installer behavior when user opt-out to upgrade to PHP7 via REMI confirms installer behavior when user opt-out to upgrade to PHP7 via REMI
''' '''
# first we will install the default php version to test installer behavior # first we will install the default php version to test installer behavior
php_install = Pihole.run('yum install -y php') php_install = host.run('yum install -y php')
assert php_install.rc == 0 assert php_install.rc == 0
php_package = Pihole.package('php') php_package = host.package('php')
default_centos_php_version = php_package.version.split('.')[0] default_centos_php_version = php_package.version.split('.')[0]
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
pytest.skip("Test deprecated . Detected default PHP version >= 7") pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Cancel for user prompt # Whiptail dialog returns Cancel for user prompt
mock_command('whiptail', {'*': ('', '1')}, Pihole) mock_command('whiptail', {'*': ('', '1')}, host)
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -86,24 +86,24 @@ def test_php_version_lt_7_detected_upgrade_user_optout_centos(Pihole):
expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. ' expected_stdout = info_box + (' User opt-out of PHP 7 upgrade on CentOS. '
'Deprecated PHP may be in use.') 'Deprecated PHP may be in use.')
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed
def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole): def test_php_version_lt_7_detected_upgrade_user_optin_centos(host):
''' '''
confirms installer behavior when user opt-in to upgrade to PHP7 via REMI confirms installer behavior when user opt-in to upgrade to PHP7 via REMI
''' '''
# first we will install the default php version to test installer behavior # first we will install the default php version to test installer behavior
php_install = Pihole.run('yum install -y php') php_install = host.run('yum install -y php')
assert php_install.rc == 0 assert php_install.rc == 0
php_package = Pihole.package('php') php_package = host.package('php')
default_centos_php_version = php_package.version.split('.')[0] default_centos_php_version = php_package.version.split('.')[0]
if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended if int(default_centos_php_version) >= 7: # PHP7 is supported/recommended
pytest.skip("Test deprecated . Detected default PHP version >= 7") pytest.skip("Test deprecated . Detected default PHP version >= 7")
# Whiptail dialog returns Continue for user prompt # Whiptail dialog returns Continue for user prompt
mock_command('whiptail', {'*': ('', '0')}, Pihole) mock_command('whiptail', {'*': ('', '0')}, host)
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
@ -118,8 +118,8 @@ def test_php_version_lt_7_detected_upgrade_user_optin_centos(Pihole):
expected_stdout = tick_box + (' Remi\'s RPM repository has ' expected_stdout = tick_box + (' Remi\'s RPM repository has '
'been enabled for PHP7') 'been enabled for PHP7')
assert expected_stdout in package_manager_detect.stdout assert expected_stdout in package_manager_detect.stdout
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert remi_package.is_installed assert remi_package.is_installed
updated_php_package = Pihole.package('php') updated_php_package = host.package('php')
updated_php_version = updated_php_package.version.split('.')[0] updated_php_version = updated_php_package.version.split('.')[0]
assert int(updated_php_version) == 7 assert int(updated_php_version) == 7

@ -5,7 +5,7 @@ from .conftest import (
) )
def mock_selinux_config(state, Pihole): def mock_selinux_config(state, host):
''' '''
Creates a mock SELinux config file with expected content Creates a mock SELinux config file with expected content
''' '''
@ -13,20 +13,20 @@ def mock_selinux_config(state, Pihole):
valid_states = ['enforcing', 'permissive', 'disabled'] valid_states = ['enforcing', 'permissive', 'disabled']
assert state in valid_states assert state in valid_states
# getenforce returns the running state of SELinux # getenforce returns the running state of SELinux
mock_command('getenforce', {'*': (state.capitalize(), '0')}, Pihole) mock_command('getenforce', {'*': (state.capitalize(), '0')}, host)
# create mock configuration with desired content # create mock configuration with desired content
Pihole.run(''' host.run('''
mkdir /etc/selinux mkdir /etc/selinux
echo "SELINUX={state}" > /etc/selinux/config echo "SELINUX={state}" > /etc/selinux/config
'''.format(state=state.lower())) '''.format(state=state.lower()))
def test_selinux_enforcing_exit(Pihole): def test_selinux_enforcing_exit(host):
''' '''
confirms installer prompts to exit when SELinux is Enforcing by default confirms installer prompts to exit when SELinux is Enforcing by default
''' '''
mock_selinux_config("enforcing", Pihole) mock_selinux_config("enforcing", host)
check_selinux = Pihole.run(''' check_selinux = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
checkSelinux checkSelinux
''') ''')
@ -37,12 +37,12 @@ def test_selinux_enforcing_exit(Pihole):
assert check_selinux.rc == 1 assert check_selinux.rc == 1
def test_selinux_permissive(Pihole): def test_selinux_permissive(host):
''' '''
confirms installer continues when SELinux is Permissive confirms installer continues when SELinux is Permissive
''' '''
mock_selinux_config("permissive", Pihole) mock_selinux_config("permissive", host)
check_selinux = Pihole.run(''' check_selinux = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
checkSelinux checkSelinux
''') ''')
@ -51,12 +51,12 @@ def test_selinux_permissive(Pihole):
assert check_selinux.rc == 0 assert check_selinux.rc == 0
def test_selinux_disabled(Pihole): def test_selinux_disabled(host):
''' '''
confirms installer continues when SELinux is Disabled confirms installer continues when SELinux is Disabled
''' '''
mock_selinux_config("disabled", Pihole) mock_selinux_config("disabled", host)
check_selinux = Pihole.run(''' check_selinux = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
checkSelinux checkSelinux
''') ''')

@ -1,16 +1,16 @@
def test_epel_and_remi_not_installed_fedora(Pihole): def test_epel_and_remi_not_installed_fedora(host):
''' '''
confirms installer does not attempt to install EPEL/REMI repositories confirms installer does not attempt to install EPEL/REMI repositories
on Fedora on Fedora
''' '''
package_manager_detect = Pihole.run(''' package_manager_detect = host.run('''
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
select_rpm_php select_rpm_php
''') ''')
assert package_manager_detect.stdout == '' assert package_manager_detect.stdout == ''
epel_package = Pihole.package('epel-release') epel_package = host.package('epel-release')
assert not epel_package.is_installed assert not epel_package.is_installed
remi_package = Pihole.package('remi-release') remi_package = host.package('remi-release')
assert not remi_package.is_installed assert not remi_package.is_installed

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

@ -1,5 +1,5 @@
[tox] [tox]
envlist = py37 envlist = py38
[testenv] [testenv]
whitelist_externals = docker whitelist_externals = docker

Loading…
Cancel
Save