diff options
author | Jacob Salmela <jacob.salmela@pi-hole.net> | 2018-12-09 23:33:58 +0300 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-12-09 23:33:58 +0300 |
commit | f65f793a0d0d7dbc5882be38858c986f42b8c776 (patch) | |
tree | cb921f69dd95c09e5c9070ead83425d64b4d1b1d | |
parent | ddbdb51d209fa9eea2451a1bebfaeaef23a9b77b (diff) | |
parent | f7ad4898056d3836c4a8533c73bb08824702142b (diff) |
Merge pull request #2496 from pi-hole/release/v4.1v4.1
Release v4.1
-rw-r--r-- | README.md | 72 | ||||
-rw-r--r-- | advanced/01-pihole.conf | 23 | ||||
-rwxr-xr-x | advanced/Scripts/chronometer.sh | 5 | ||||
-rwxr-xr-x | advanced/Scripts/list.sh | 1 | ||||
-rwxr-xr-x | advanced/Scripts/piholeDebug.sh | 24 | ||||
-rwxr-xr-x | advanced/Scripts/piholeLogFlush.sh | 2 | ||||
-rwxr-xr-x | advanced/Scripts/updatecheck.sh | 43 | ||||
-rwxr-xr-x | advanced/Scripts/webpage.sh | 28 | ||||
-rw-r--r-- | advanced/Templates/pihole-FTL.conf | 84 | ||||
-rw-r--r-- | advanced/Templates/pihole-FTL.service | 21 | ||||
-rw-r--r-- | advanced/Templates/pihole.cron | 4 | ||||
-rw-r--r-- | advanced/index.php | 2 | ||||
-rw-r--r-- | advanced/lighttpd.conf.debian | 1 | ||||
-rw-r--r-- | advanced/lighttpd.conf.fedora | 1 | ||||
-rwxr-xr-x | automated install/basic-install.sh | 866 | ||||
-rwxr-xr-x | automated install/uninstall.sh | 2 | ||||
-rwxr-xr-x | gravity.sh | 65 | ||||
-rwxr-xr-x | pihole | 57 | ||||
-rw-r--r-- | test/test_automated_install.py | 7 | ||||
-rw-r--r-- | test/test_centos_fedora_support.py | 13 |
20 files changed, 737 insertions, 584 deletions
@@ -22,7 +22,7 @@ The Pi-hole[®](https://pi-hole.net/trademark-rules-and-brand-guidelines/) is a <a href="https://www.bountysource.com/trackers/3011939-pi-hole-pi-hole?utm_source=3011939&utm_medium=shield&utm_campaign=TRACKER_BADGE"><img src="https://www.bountysource.com/badge/tracker?tracker_id=3011939" alt="BountySource"/></a> ## One-Step Automated Install -Those who want to get started quickly and conveniently, may install Pi-hole using the following command: +Those who want to get started quickly and conveniently may install Pi-hole using the following command: #### `curl -sSL https://install.pi-hole.net | bash` @@ -46,14 +46,14 @@ sudo bash basic-install.sh Once the installer has been run, you will need to [configure your router to have **DHCP clients use Pi-hole as their DNS server**](https://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245) which ensures that all devices connecting to your network will have content blocked without any further intervention. -If your router does not support setting the DNS server, you can [use Pi-hole's built in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); just be sure to disable DHCP on your router first (if it has that feature available). +If your router does not support setting the DNS server, you can [use Pi-hole's built-in DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026); just be sure to disable DHCP on your router first (if it has that feature available). As a last resort, you can always manually set each device to use Pi-hole as their DNS server. ----- ## Pi-hole is free, but powered by your support -There are many reoccurring costs involved with maintaining free, open source, and privacy respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained. +There are many reoccurring costs involved with maintaining free, open source, and privacy-respecting software; expenses which [our volunteer developers](https://github.com/orgs/pi-hole/people) pitch in to cover out-of-pocket. This is just one example of how strongly we feel about our software, as well as the importance of keeping it maintained. Make no mistake: **your support is absolutely vital to help keep us innovating!** @@ -70,7 +70,7 @@ Sending a donation using our links below is **extremely helpful** in offsetting If you'd rather not [donate](https://pi-hole.net/donate/) (_which is okay!_), there are other ways you can help support us: - [Patreon](https://patreon.com/pihole) _Become a patron for rewards_ - [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) _affiliate link_ -- [UNIXstickers.com](http://unixstickers.refr.cc/jacobs) _save $5 when you spend $9 using our affiliate link_ +- [Stickermule](https://www.stickermule.com/unlock?ref_id=6055890701&utm_medium=link&utm_source=invite) _earn a $10 credit after your first purchase_ - [Pi-hole Swag Store](https://pi-hole.net/shop/) _affiliate link_ - [Amazon](http://www.amazon.com/exec/obidos/redirect-home/pihole09-20) _affiliate link_ - [DNS Made Easy](https://cp.dnsmadeeasy.com/u/133706) _affiliate link_ @@ -82,7 +82,7 @@ We welcome _everyone_ to contribute to issue reports, suggest new features, and If you have something to add - anything from a typo through to a whole new feature, we're happy to check it out! Just make sure to fill out our template when submitting your request; the questions that it asks will help the volunteers quickly understand what you're aiming to achieve. -You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it, and submit a pull request for us to review. +You'll find that the [install script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) and the [debug script](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/piholeDebug.sh) have an abundance of comments, which will help you better understand how Pi-hole works. They're also a valuable resource to those who want to learn how to write scripts or code a program! We encourage anyone who likes to tinker to read through it and submit a pull request for us to review. ### Presentations about Pi-hole Word-of-mouth continues to help our project grow immensely, and so we are helping make this easier for people. @@ -110,7 +110,7 @@ While we are primarily reachable on our <a href="https://discourse.pi-hole.net/" ## Breakdown of Features ### The Command Line Interface -The `pihole` command has all the functionality necessary to be able to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with understanding of `bash`. +The `pihole` command has all the functionality necessary to be able to fully administer the Pi-hole, without the need of the Web Interface. It's fast, user-friendly, and auditable by anyone with an understanding of `bash`. <a href="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif"><img src="https://pi-hole.github.io/graphics/Screenshots/blacklist-cli.gif" alt="Pi-hole Blacklist Demo"/></a> @@ -137,7 +137,7 @@ Some notable features include: * Detailed graphs and doughnut charts * Top lists of domains and clients * A filterable and sortable query log -* Long Term Statistics to view data over user defined time ranges +* Long Term Statistics to view data over user-defined time ranges * The ability to easily manage and configure Pi-hole features * ... and all the main features of the Command Line Interface! @@ -148,7 +148,7 @@ There are several ways to [access the dashboard](https://discourse.pi-hole.net/t 3. `http://pi.hole/` (when using Pi-hole as your DNS server) ## Faster-than-light Engine -FTLDNS[™](https://pi-hole.net/trademark-rules-and-brand-guidelines/) is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*! +FTLDNS is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTLDNS does this all *very quickly*! Some of the statistics you can integrate include: * Total number of domains being blocked @@ -165,40 +165,46 @@ The API can be accessed via [`telnet`](https://github.com/pi-hole/FTL), the Web ----- ## The Origin Of Pi-hole -Pi-hole being a **advertising-aware DNS/Web server**, makes use of the following technologies: +Pi-hole being an **advertising-aware DNS/Web server**, makes use of the following technologies: * [`dnsmasq`](http://www.thekelleys.org.uk/dnsmasq/doc.html) - a lightweight DNS and DHCP server * [`curl`](https://curl.haxx.se) - A command line tool for transferring data with URL syntax -* [`lighttpd`](https://www.lighttpd.net) - webserver designed and optimized for high performance +* [`lighttpd`](https://www.lighttpd.net) - web server designed and optimized for high performance * [`php`](https://secure.php.net) - a popular general-purpose web scripting language * [AdminLTE Dashboard](https://github.com/almasaeed2010/AdminLTE) - premium admin control panel based on Bootstrap 3.x -While quite outdated at this point, [this original blog post about Pi-hole](https://jacobsalmela.com/2015/06/16/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/) goes into **great detail** about how Pi-hole was originally setup and how it works. Syntactically, it's no longer accurate, but the same basic principles and logic still apply to Pi-hole's current state. +While quite outdated at this point, [this original blog post about Pi-hole](https://jacobsalmela.com/2015/06/16/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0/) goes into **great detail** about how Pi-hole was originally set up and how it works. Syntactically, it's no longer accurate, but the same basic principles and logic still apply to Pi-hole's current state. + ----- ## Coverage -- [Software Engineering Daily: Interview with the creator of Pi-hole](https://softwareengineeringdaily.com/2018/05/29/pi-hole-ad-blocker-hardware-with-jacob-salmela/) -- [Bloomberg Business Week: Brotherhood of the Ad blockers](https://www.bloomberg.com/news/features/2018-05-10/inside-the-brotherhood-of-pi-hole-ad-blockers) -- [Securing DNS across all of my devices with Pi-Hole + DNS-over-HTTPS + 1.1.1.1](https://scotthelme.co.uk/securing-dns-across-all-of-my-devices-with-pihole-dns-over-https-1-1-1-1/) -- [Adafruit: installing Pi-hole on a Pi Zero W](https://learn.adafruit.com/pi-hole-ad-blocker-with-pi-zero-w/install-pi-hole) -- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) -- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) -- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) -- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) -- [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m) -- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s) -- [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d) -- [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/) -- [MacObserver Podcast 585](https://www.macobserver.com/tmo/podcast/macgeekgab-585) -- [The Defrag Show: Endoscope USB Camera, The Final [HoloLens] Vote, Adblock Pi and more](https://channel9.msdn.com/Shows/The-Defrag-Show/Defrag-Endoscope-USB-Camera-The-Final-HoloLens-Vote-Adblock-Pi-and-more?WT.mc_id=dlvr_twitter_ch9#time=20m39s) -- [Adafruit: Pi-hole is a black hole for internet ads](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/) -- [Digital Trends: 5 Fun, Easy Projects You Can Try With a $35 Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s) -- [Adafruit: Raspberry Pi Quick Look at Pi Hole ad blocking server with Tony D](https://www.youtube.com/watch?v=eg4u2j1HYlI) -- [Devacron: OrangePi Zero as an Ad-Block server with Pi-Hole](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/) -- [Linux Pro: The Hole Truth](http://www.linuxpromagazine.com/Issues/2017/200/The-sysadmin-s-daily-grind-Pi-hole) -- [CryptoAUSTRALIA: How We Tried 5 Privacy Focused Raspberry Pi Projects](https://blog.cryptoaustralia.org.au/2017/10/05/5-privacy-focused-raspberry-pi-projects/) -- [CryptoAUSTRALIA: Pi-hole Workshop](https://blog.cryptoaustralia.org.au/2017/11/02/pi-hole-network-wide-ad-blocker/) -- [Know How 355: Killing ads with a Raspberry Pi-Hole!](https://www.twit.tv/shows/know-how/episodes/355) +- [Lifehacker: Turn A Raspberry Pi Into An Ad Blocker With A Single Command](https://www.lifehacker.com.au/2015/02/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-command/) (Feburary, 2015) +- [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/) (March, 2015) +- [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) (September, 2015) +- [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s) (October, 2015) +- [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m) (December, 2015) +- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s) (December, 2015) +- [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d) (December, 2015) +- [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/) (December, 2015) +- [MacObserver Podcast 585](https://www.macobserver.com/tmo/podcast/macgeekgab-585) (December, 2015) +- [The Defrag Show: Endoscope USB Camera, The Final [HoloLens] Vote, Adblock Pi and more](https://channel9.msdn.com/Shows/The-Defrag-Show/Defrag-Endoscope-USB-Camera-The-Final-HoloLens-Vote-Adblock-Pi-and-more?WT.mc_id=dlvr_twitter_ch9#time=20m39s) (January, 2016) +- [Adafruit: Pi-hole is a black hole for internet ads](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/) (March, 2016) +- [Digital Trends: 5 Fun, Easy Projects You Can Try With a $35 Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s) (March, 2016) +- [Adafruit: Raspberry Pi Quick Look at Pi Hole ad blocking server with Tony D](https://www.youtube.com/watch?v=eg4u2j1HYlI) (June, 2016) +- [Devacron: OrangePi Zero as an Ad-Block server with Pi-Hole](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/) (December, 2016) +- [Linux Pro: The Hole Truth](http://www.linuxpromagazine.com/Issues/2017/200/The-sysadmin-s-daily-grind-Pi-hole) (July, 2017) +- [Adafruit: installing Pi-hole on a Pi Zero W](https://learn.adafruit.com/pi-hole-ad-blocker-with-pi-zero-w/install-pi-hole) (August, 2017) +- [CryptoAUSTRALIA: How We Tried 5 Privacy Focused Raspberry Pi Projects](https://blog.cryptoaustralia.org.au/2017/10/05/5-privacy-focused-raspberry-pi-projects/) (October, 2017) +- [CryptoAUSTRALIA: Pi-hole Workshop](https://blog.cryptoaustralia.org.au/2017/11/02/pi-hole-network-wide-ad-blocker/) (November, 2017) +- [Know How 355: Killing ads with a Raspberry Pi-Hole!](https://www.twit.tv/shows/know-how/episodes/355) (November, 2017) +- [Hobohouse: Block Advertising on your Network with Pi-hole and Raspberry Pi](https://hobo.house/2018/02/27/block-advertising-with-pi-hole-and-raspberry-pi/) (March, 2018) +- [Scott Helme: Securing DNS across all of my devices with Pi-Hole + DNS-over-HTTPS + 1.1.1.1](https://scotthelme.co.uk/securing-dns-across-all-of-my-devices-with-pihole-dns-over-https-1-1-1-1/) (April, 2018) +- [Scott Helme: Catching and dealing with naughty devices on my home network](https://scotthelme.co.uk/catching-naughty-devices-on-my-home-network/) (April, 2018) +- [Bloomberg Business Week: Brotherhood of the Ad blockers](https://www.bloomberg.com/news/features/2018-05-10/inside-the-brotherhood-of-pi-hole-ad-blockers) (May, 2018) +- [Software Engineering Daily: Interview with the creator of Pi-hole](https://softwareengineeringdaily.com/2018/05/29/pi-hole-ad-blocker-hardware-with-jacob-salmela/) (May, 2018) +- [Raspberry Pi: Block ads at home using Pi-hole and a Raspberry Pi](https://www.raspberrypi.org/blog/pi-hole-raspberry-pi/) (July, 2018) +- [Troy Hunt: Mmm... Pi-hole...](https://www.troyhunt.com/mmm-pi-hole/) (September, 2018) +- [PEBKAK Podcast: Interview With Jacob Salmela](https://www.jerseystudios.net/2018/10/11/150-pi-hole/) (October, 2018) ----- diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index 85d260b2..3f4e4bc5 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -1,13 +1,11 @@ # Pi-hole: A black hole for Internet advertisements -# (c) 2015, 2016 by Jacob Salmela -# Network-wide ad blocking via your Raspberry Pi -# http://pi-hole.net -# dnsmasq config for Pi-hole +# (c) 2017 Pi-hole, LLC (https://pi-hole.net) +# Network-wide ad blocking via your own hardware. # -# Pi-hole is free software: you can redistribute it and/or modify -# it under the terms of the GNU General Public License as published by -# the Free Software Foundation, either version 2 of the License, or -# (at your option) any later version. +# Dnsmasq config for Pi-hole's FTLDNS +# +# This file is copyright under the latest version of the EUPL. +# Please see LICENSE file for your rights under this license. ############################################################################### # FILE AUTOMATICALLY POPULATED BY PI-HOLE INSTALL/UPDATE PROCEDURE. # @@ -16,14 +14,17 @@ # IF YOU WISH TO CHANGE THE UPSTREAM SERVERS, CHANGE THEM IN: # # /etc/pihole/setupVars.conf # # # -# ANY OTHER CHANGES SHOULD BE MADE IN A SEPERATE CONFIG FILE # -# OR IN /etc/dnsmasq.conf # +# ANY OTHER CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE # +# WITHIN /etc/dnsmasq.d/yourname.conf # ############################################################################### addn-hosts=/etc/pihole/gravity.list addn-hosts=/etc/pihole/black.list addn-hosts=/etc/pihole/local.list +user=pihole +group=pihole + domain-needed localise-queries @@ -39,7 +40,7 @@ interface=@INT@ cache-size=10000 -log-queries=extra +log-queries log-facility=/var/log/pihole.log local-ttl=2 diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 046a98c4..7c2d8b52 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -8,6 +8,7 @@ # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. +LC_ALL=C LC_NUMERIC=C # Retrieve stats from FTL engine @@ -243,7 +244,7 @@ get_sys_stats() { disk_total="${disk_raw[1]}" disk_perc="${disk_raw[2]}" - net_gateway=$(route -n | awk '$4 == "UG" {print $2;exit}') + net_gateway=$(ip route | grep default | cut -d ' ' -f 3 | head -n 1) # Get DHCP stats, if feature is enabled if [[ "$DHCP_ACTIVE" == "true" ]]; then @@ -478,7 +479,7 @@ chronoFunc() { ${COL_LIGHT_RED}Press Ctrl-C to exit${COL_NC} ${COL_DARK_GRAY}$scr_line_str${COL_NC}" else - echo -e "[0;1;31;91m|¯[0;1;33;93m¯[0;1;32;92m¯[0;1;32;92m(¯[0;1;36;96m)[0;1;34;94m_[0;1;35;95m|[0;1;33;93m¯[0;1;31;91m|_ [0;1;32;92m__[0;1;36;96m_|[0;1;31;91m¯[0;1;34;94m|[0;1;35;95m__[0;1;31;91m_[0m$phc_ver_str[0;1;33;93m| ¯[0;1;32;92m_[0;1;36;96m/¯[0;1;34;94m|[0;1;35;95m_[0;1;31;91m| [0;1;33;93m' [0;1;32;92m\\/ [0;1;36;96m_ [0;1;34;94m\\ [0;1;35;95m/ [0;1;31;91m-[0;1;33;93m_)[0m$lte_ver_str[0;1;32;92m|_[0;1;36;96m| [0;1;34;94m|_[0;1;35;95m| [0;1;33;93m|_[0;1;32;92m||[0;1;36;96m_\\[0;1;34;94m__[0;1;35;95m_/[0;1;31;91m_\\[0;1;33;93m__[0;1;32;92m_|[0m$ftl_ver_str ${COL_DARK_GRAY}$scr_line_str${COL_NC}" + echo -e "[0;1;31;91m|¯[0;1;33;93m¯[0;1;32;92m¯[0;1;32;92m(¯[0;1;36;96m)[0;1;34;94m_[0;1;35;95m|[0;1;33;93m¯[0;1;31;91m|_ [0;1;32;92m__[0;1;36;96m_|[0;1;31;91m¯[0;1;34;94m|[0;1;35;95m__[0;1;31;91m_[0m$phc_ver_str\\n[0;1;33;93m| ¯[0;1;32;92m_[0;1;36;96m/¯[0;1;34;94m|[0;1;35;95m_[0;1;31;91m| [0;1;33;93m' [0;1;32;92m\\/ [0;1;36;96m_ [0;1;34;94m\\ [0;1;35;95m/ [0;1;31;91m-[0;1;33;93m_)[0m$lte_ver_str\\n[0;1;32;92m|_[0;1;36;96m| [0;1;34;94m|_[0;1;35;95m| [0;1;33;93m|_[0;1;32;92m||[0;1;36;96m_\\[0;1;34;94m__[0;1;35;95m_/[0;1;31;91m_\\[0;1;33;93m__[0;1;32;92m_|[0m$ftl_ver_str\\n ${COL_DARK_GRAY}$scr_line_str${COL_NC}" fi printFunc " Hostname: " "$sys_name" "$host_info" diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 87e4ab44..c1d95aae 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -13,6 +13,7 @@ basename=pihole piholeDir=/etc/"${basename}" whitelist="${piholeDir}"/whitelist.txt blacklist="${piholeDir}"/blacklist.txt + readonly regexlist="/etc/pihole/regex.list" reload=false addmode=true diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index b8377f73..cb66f97c 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -119,7 +119,7 @@ PIHOLE_WEB_SERVER_ERROR_LOG_FILE="${WEB_SERVER_LOG_DIRECTORY}/error.log" #SUPPORTED_OS=("Raspbian" "Ubuntu" "Fedora" "Debian" "CentOS") # Store Pi-hole's processes in an array for easy use and parsing -PIHOLE_PROCESSES=( "dnsmasq" "lighttpd" "pihole-FTL" ) +PIHOLE_PROCESSES=( "lighttpd" "pihole-FTL" ) # Store the required directories in an array so it can be parsed through #REQUIRED_DIRECTORIES=("${CORE_GIT_DIRECTORY}" @@ -337,8 +337,6 @@ get_program_version() { case "${program_name}" in "lighttpd") program_version="$(${program_name} -v |& head -n1 | cut -d '/' -f2 | cut -d ' ' -f1)" ;; - "dnsmasq") program_version="$(${program_name} -v |& head -n1 | awk '{print $3}')" - ;; "php") program_version="$(${program_name} -v |& head -n1 | cut -d '-' -f1 | cut -d ' ' -f2)" ;; # If a match is not found, show an error @@ -358,7 +356,6 @@ get_program_version() { # and their versions, using the functions above. check_critical_program_versions() { # Use the function created earlier and bundle them into one function that checks all the version numbers - get_program_version "dnsmasq" get_program_version "lighttpd" get_program_version "php" } @@ -640,11 +637,12 @@ ping_internet() { compare_port_to_service_assigned() { local service_name="${1}" # The programs we use may change at some point, so they are in a varible here - local resolver="dnsmasq" + local resolver="pihole-FTL" local web_server="lighttpd" local ftl="pihole-FTL" + + # If the service is a Pi-hole service, highlight it in green if [[ "${service_name}" == "${resolver}" ]] || [[ "${service_name}" == "${web_server}" ]] || [[ "${service_name}" == "${ftl}" ]]; then - # if port 53 is dnsmasq, show it in green as it's standard log_write "[${COL_GREEN}${port_number}${COL_NC}] is in use by ${COL_GREEN}${service_name}${COL_NC}" # Otherwise, else @@ -657,7 +655,7 @@ check_required_ports() { echo_current_diagnostic "Ports in use" # Since Pi-hole needs 53, 80, and 4711, check what they are being used by # so we can detect any issues - local resolver="dnsmasq" + local resolver="pihole-FTL" local web_server="lighttpd" local ftl="pihole-FTL" # Create an array for these ports in use @@ -682,7 +680,7 @@ check_required_ports() { continue fi # Use a case statement to determine if the right services are using the right ports - case "${port_number}" in + case "$(echo "$port_number" | rev | cut -d: -f1 | rev)" in 53) compare_port_to_service_assigned "${resolver}" ;; 80) compare_port_to_service_assigned "${web_server}" @@ -836,9 +834,13 @@ process_status(){ local status_of_process status_of_process=$(systemctl is-active "${i}") else - # Otherwise, use the service command + # Otherwise, use the service command and mock the output of `systemctl is-active` local status_of_process - status_of_process=$(service "${i}" status | awk '/Active:/ {print $2}') &> /dev/null + if service "${i}" status | grep -E 'is\srunning' &> /dev/null; then + status_of_process="active" + else + status_of_process="inactive" + fi fi # and print it out to the user if [[ "${status_of_process}" == "active" ]]; then @@ -907,7 +909,7 @@ parse_file() { #shellcheck disable=SC2016 IFS=$'\r\n' command eval 'file_info=( $(cat "${filename}") )' else - read -a file_info <<< $filename + read -r -a file_info <<< "$filename" fi # Set a named variable for better readability local file_lines diff --git a/advanced/Scripts/piholeLogFlush.sh b/advanced/Scripts/piholeLogFlush.sh index 4847282f..561fbce7 100755 --- a/advanced/Scripts/piholeLogFlush.sh +++ b/advanced/Scripts/piholeLogFlush.sh @@ -58,6 +58,8 @@ else # Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history) deleted=$(sqlite3 "${DBFILE}" "DELETE FROM queries WHERE timestamp >= strftime('%s','now')-86400; select changes() from queries limit 1") + # Restart pihole-FTL to force reloading history + sudo pihole restartdns fi if [[ "$@" != *"quiet"* ]]; then diff --git a/advanced/Scripts/updatecheck.sh b/advanced/Scripts/updatecheck.sh index 767c5461..257c1929 100755 --- a/advanced/Scripts/updatecheck.sh +++ b/advanced/Scripts/updatecheck.sh @@ -37,30 +37,55 @@ function get_local_version() { git describe --long --dirty --tags || return 1 } +# Source the setupvars config file +# shellcheck disable=SC1091 +. /etc/pihole/setupVars.conf + if [[ "$2" == "remote" ]]; then if [[ "$3" == "reboot" ]]; then sleep 30 fi - GITHUB_CORE_VERSION="$(json_extract tag_name "$(curl -q 'https://api.github.com/repos/pi-hole/pi-hole/releases/latest' 2> /dev/null)")" - GITHUB_WEB_VERSION="$(json_extract tag_name "$(curl -q 'https://api.github.com/repos/pi-hole/AdminLTE/releases/latest' 2> /dev/null)")" - GITHUB_FTL_VERSION="$(json_extract tag_name "$(curl -q 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null)")" + GITHUB_VERSION_FILE="/etc/pihole/GitHubVersions" + + GITHUB_CORE_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/pi-hole/releases/latest' 2> /dev/null)")" + echo -n "${GITHUB_CORE_VERSION}" > "${GITHUB_VERSION_FILE}" + + if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then + GITHUB_WEB_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/AdminLTE/releases/latest' 2> /dev/null)")" + echo -n " ${GITHUB_WEB_VERSION}" >> "${GITHUB_VERSION_FILE}" + fi - echo -n "${GITHUB_CORE_VERSION} ${GITHUB_WEB_VERSION} ${GITHUB_FTL_VERSION}" > "/etc/pihole/GitHubVersions" + GITHUB_FTL_VERSION="$(json_extract tag_name "$(curl -s 'https://api.github.com/repos/pi-hole/FTL/releases/latest' 2> /dev/null)")" + echo -n " ${GITHUB_FTL_VERSION}" >> "${GITHUB_VERSION_FILE}" else + LOCAL_BRANCH_FILE="/etc/pihole/localbranches" + CORE_BRANCH="$(get_local_branch /etc/.pihole)" - WEB_BRANCH="$(get_local_branch /var/www/html/admin)" + echo -n "${CORE_BRANCH}" > "${LOCAL_BRANCH_FILE}" + + if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then + WEB_BRANCH="$(get_local_branch /var/www/html/admin)" + echo -n " ${WEB_BRANCH}" >> "${LOCAL_BRANCH_FILE}" + fi + FTL_BRANCH="$(pihole-FTL branch)" + echo -n " ${FTL_BRANCH}" >> "${LOCAL_BRANCH_FILE}" - echo -n "${CORE_BRANCH} ${WEB_BRANCH} ${FTL_BRANCH}" > "/etc/pihole/localbranches" + LOCAL_VERSION_FILE="/etc/pihole/localversions" CORE_VERSION="$(get_local_version /etc/.pihole)" - WEB_VERSION="$(get_local_version /var/www/html/admin)" - FTL_VERSION="$(pihole-FTL version)" + echo -n "${CORE_VERSION}" > "${LOCAL_VERSION_FILE}" - echo -n "${CORE_VERSION} ${WEB_VERSION} ${FTL_VERSION}" > "/etc/pihole/localversions" + if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then + WEB_VERSION="$(get_local_version /var/www/html/admin)" + echo -n " ${WEB_VERSION}" >> "${LOCAL_VERSION_FILE}" + fi + + FTL_VERSION="$(pihole-FTL version)" + echo -n " ${FTL_VERSION}" >> "${LOCAL_VERSION_FILE}" fi diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index c3dede05..0b5c351e 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -110,7 +110,7 @@ SetWebPassword() { # Prevents a bug if the user presses Ctrl+C and it continues to hide the text typed. # So we reset the terminal via stty if the user does press Ctrl+C trap '{ echo -e "\nNo password will be set" ; stty sane ; exit 1; }' INT - read -s -p "Enter New Password (Blank for no password): " PASSWORD + read -s -r -p "Enter New Password (Blank for no password): " PASSWORD echo "" if [ "${PASSWORD}" == "" ]; then @@ -119,12 +119,13 @@ SetWebPassword() { exit 0 fi - read -s -p "Confirm Password: " CONFIRM + read -s -r -p "Confirm Password: " CONFIRM echo "" fi if [ "${PASSWORD}" == "${CONFIRM}" ] ; then - hash=$(HashPassword "${PASSWORD}") + # We do not wrap this in brackets, otherwise BASH will expand any appropriate syntax + hash=$(HashPassword "$PASSWORD") # Save hash to file change_setting "WEBPASSWORD" "${hash}" echo -e " ${TICK} New password set" @@ -525,14 +526,24 @@ Teleporter() { php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip" } -audit() +addAudit() { - echo "${args[2]}" >> /etc/pihole/auditlog.list + shift # skip "-a" + shift # skip "audit" + for var in "$@" + do + echo "${var}" >> /etc/pihole/auditlog.list + done +} + +clearAudit() +{ + echo -n "" > /etc/pihole/auditlog.list } SetPrivacyLevel() { - # Set privacy level. Minimum is 0, maximum is 3 - if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 3 ]; then + # Set privacy level. Minimum is 0, maximum is 4 + if [ "${args[2]}" -ge 0 ] && [ "${args[2]}" -le 4 ]; then changeFTLsetting "PRIVACYLEVEL" "${args[2]}" fi } @@ -565,7 +576,8 @@ main() { "-i" | "interface" ) SetListeningMode "$@";; "-t" | "teleporter" ) Teleporter;; "adlist" ) CustomizeAdLists;; - "audit" ) audit;; + "audit" ) addAudit "$@";; + "clearaudit" ) clearAudit;; "-l" | "privacylevel" ) SetPrivacyLevel;; * ) helpFunc;; esac diff --git a/advanced/Templates/pihole-FTL.conf b/advanced/Templates/pihole-FTL.conf deleted file mode 100644 index 03f42932..00000000 --- a/advanced/Templates/pihole-FTL.conf +++ /dev/null @@ -1,84 +0,0 @@ -### This file contains parameters for FTL behavior. -### At install, all parameters are commented out. The user can select desired options. -### Options shown are the default configuration. No modification is needed for most -### installations. -### Visit https://docs.pi-hole.net/ftldns/configfile/ for more detailed parameter explanations - -## Socket Listening -## Listen only for local socket connections or permit all connections -## Options: localonly, all -#SOCKET_LISTENING=localonly - -## Query Display -## Display all queries? Set to no to hide query display -## Options: yes, no -#QUERY_DISPLAY=yes - -## AAA Query Analysis -## Allow FTL to analyze AAAA queries from pihole.log? -## Options: yes, no -#AAAA_QUERY_ANALYSIS=yes - -## Resolve IPv6 -## Should FTL try to resolve IPv6 addresses to host names? -## Options: yes, no -#RESOLVE_IPV6=yes - -## Resolve IPv4 -## Should FTL try to resolve IPv4 addresses to host names? -## Options: yes, no -#RESOLVE_IPV4=yes - -## Max Database Days -## How long should queries be stored in the database (days)? -## Setting this to 0 disables the database -## See: https://docs.pi-hole.net/ftldns/database/ -## Options: number of days -#MAXDBDAYS=365 - -## Database Interval -## How often do we store queries in FTL's database (minutes)? -## See: https://docs.pi-hole.net/ftldns/database/ -## Options: number of minutes -#DBINTERVAL=1.0 - -## Database File -## Specify path and filename of FTL's SQLite3 long-term database. -## Setting this to DBFILE= disables the database altogether -## See: https://docs.pi-hole.net/ftldns/database/ -## Option: path to db file -#DBFILE=/etc/pihole/pihole-FTL.db - -## Max Log Age -## Up to how many hours of queries should be imported from the database and logs (hours)? -## Maximum is 744 (31 days) -## Options: number of days -#MAXLOGAGE=24.0 - -## FTL Port -## On which port should FTL be listening? -## Options: tcp port -#FTLPORT=4711 - -## Privacy Level -## Which privacy level is used? -## See: https://docs.pi-hole.net/ftldns/privacylevels/ -## Options: 0, 1, 2, 3 -#PRIVACYLEVEL=0 - -## Ignore Localhost -## Should FTL ignore queries coming from the local machine? -## Options: yes, no -#IGNORE_LOCALHOST=no - -## Blocking Mode -## How should FTL reply to blocked queries? -## See: https://docs.pi-hole.net/ftldns/blockingmode/ -## Options: NULL, IP-AAAA-NODATA, IP, NXDOMAIN -#BLOCKINGMODE=NULL - -## Regex Debug Mode -## Controls if FTLDNS should print extended details about regex matching into pihole-FTL.log. -## See: https://docs.pi-hole.net/ftldns/regex/overview/ -## Options: true, false -#REGEX_DEBUGMODE=false diff --git a/advanced/Templates/pihole-FTL.service b/advanced/Templates/pihole-FTL.service index ecc7a52a..d19e2bd5 100644 --- a/advanced/Templates/pihole-FTL.service +++ b/advanced/Templates/pihole-FTL.service @@ -26,17 +26,26 @@ start() { if is_running; then echo "pihole-FTL is already running" else - touch /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log + # Touch files to ensure they exist (create if non-existing, preserve if existing) + touch /var/log/pihole-FTL.log /var/log/pihole.log + touch /run/pihole-FTL.pid /run/pihole-FTL.port + touch /etc/pihole/dhcp.leases mkdir -p /var/run/pihole mkdir -p /var/log/pihole chown pihole:pihole /var/run/pihole /var/log/pihole rm /var/run/pihole/FTL.sock 2> /dev/null - chown pihole:pihole /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port - chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases /var/log/pihole.log + # Ensure that permissions are set so that pihole-FTL can edit all necessary files + chown pihole:pihole /run/pihole-FTL.pid /run/pihole-FTL.port + chown pihole:pihole /etc/pihole /etc/pihole/dhcp.leases 2> /dev/null + chown pihole:pihole /var/log/pihole-FTL.log /var/log/pihole.log chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /var/log/pihole.log - setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+eip "$(which pihole-FTL)" echo "nameserver 127.0.0.1" | /sbin/resolvconf -a lo.piholeFTL - su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER" + if setcap CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN+eip "$(which pihole-FTL)"; then + su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER" + else + echo "Warning: Starting pihole-FTL as root because setting capabilities is not supported on this system" + pihole-FTL + fi echo fi } @@ -78,7 +87,7 @@ status() { echo "[ ] pihole-FTL is not running" exit 1 fi -} +} ### main logic ### diff --git a/advanced/Templates/pihole.cron b/advanced/Templates/pihole.cron index 02a63b74..8dc98721 100644 --- a/advanced/Templates/pihole.cron +++ b/advanced/Templates/pihole.cron @@ -16,7 +16,9 @@ # Pi-hole: Update the ad sources once a week on Sunday at a random time in the # early morning. Download any updates from the adlists -59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity +# Squash output to log, then splat the log to stdout on error to allow for +# standard crontab job error handling. +59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity >/var/log/pihole_updateGravity.log || cat /var/log/pihole_updateGravity.log # Pi-hole: Flush the log daily at 00:00 # The flush script will use logrotate if available diff --git a/advanced/index.php b/advanced/index.php index cad59ec7..49eb0f45 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -8,6 +8,8 @@ // Sanitise HTTP_HOST output $serverName = htmlspecialchars($_SERVER["HTTP_HOST"]); +// Remove external ipv6 brackets if any +$serverName = preg_replace('/^\[(.*)\]$/', '${1}', $serverName); if (!is_file("/etc/pihole/setupVars.conf")) die("[ERROR] File not found: <code>/etc/pihole/setupVars.conf</code>"); diff --git a/advanced/lighttpd.conf.debian b/advanced/lighttpd.conf.debian index b5bece72..d9c4a110 100644 --- a/advanced/lighttpd.conf.debian +++ b/advanced/lighttpd.conf.debian @@ -72,4 +72,5 @@ $HTTP["url"] =~ "^/admin/\.(.*)" { } # Add user chosen options held in external file +# This uses include_shell instead of an include wildcard for compatibility include_shell "cat external.conf 2>/dev/null" diff --git a/advanced/lighttpd.conf.fedora b/advanced/lighttpd.conf.fedora index 43d94d84..27bc33cc 100644 --- a/advanced/lighttpd.conf.fedora +++ b/advanced/lighttpd.conf.fedora @@ -90,4 +90,5 @@ $HTTP["url"] =~ "^/admin/\.(.*)" { } # Add user chosen options held in external file +# This uses include_shell instead of an include wildcard for compatibility include_shell "cat external.conf 2>/dev/null" diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 10e43b6c..4cb2a467 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -38,14 +38,14 @@ lighttpdConfig=/etc/lighttpd/lighttpd.conf # This is a file used for the colorized output coltable=/opt/pihole/COL_TABLE -# We store several other folders and +# We store several other directories and webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git" webInterfaceDir="/var/www/html/admin" piholeGitUrl="https://github.com/pi-hole/pi-hole.git" PI_HOLE_LOCAL_REPO="/etc/.pihole" # These are the names of pi-holes files, stored in an array PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version gravity uninstall webpage) -# This folder is where the Pi-hole scripts will be installed +# This directory is where the Pi-hole scripts will be installed PI_HOLE_INSTALL_DIR="/opt/pihole" PI_HOLE_CONFIG_DIR="/etc/pihole" useUpdateVars=false @@ -59,6 +59,7 @@ IPV6_ADDRESS="" # By default, query logging is enabled and the dashboard is set to be installed QUERY_LOGGING=true INSTALL_WEB_INTERFACE=true +PRIVACY_LEVEL=0 if [ -z "${USER}" ]; then USER="$(id -un)" @@ -66,9 +67,11 @@ fi # Find the rows and columns will default to 80x24 if it can not be detected -screen_size=$(stty size 2>/dev/null || echo 24 80) -rows=$(echo "${screen_size}" | awk '{print $1}') -columns=$(echo "${screen_size}" | awk '{print $2}') +screen_size=$(stty size || printf '%d %d' 24 80) +# Set rows variable to contain first number +printf -v rows '%d' "${screen_size%% *}" +# Set columns variable to contain second number +printf -v columns '%d' "${screen_size##* }" # Divide by two so the dialogs take up half of the screen, which looks nice. r=$(( rows / 2 )) @@ -139,10 +142,19 @@ show_ascii_berry() { " } +is_command() { + # Checks for existence of string passed in as only function argument. + # Exit value of 0 when exists, 1 if not exists. Value is the result + # of the `command` shell built-in call. + local check_command="$1" + + command -v "${check_command}" >/dev/null 2>&1 +} + # Compatibility distro_check() { # If apt-get is installed, then we know it's part of the Debian family -if command -v apt-get &> /dev/null; then +if is_command apt-get ; then # Set some global variables here # We don't set them earlier since the family might be Red Hat, so these values would be different PKG_MANAGER="apt-get" @@ -163,13 +175,12 @@ if command -v apt-get &> /dev/null; then iproute_pkg="iproute" fi # Check for and determine version number (major and minor) of current php install - if command -v php &> /dev/null; then - phpInsVersion="$(php -v | head -n1 | grep -Po '(?<=PHP )[^ ]+')" - echo -e " ${INFO} Existing PHP installation detected : PHP version $phpInsVersion" - phpInsMajor="$(echo "$phpInsVersion" | cut -d\. -f1)" - phpInsMinor="$(echo "$phpInsVersion" | cut -d\. -f2)" + if is_command php ; then + 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 phpInsMinor "%d" "$(php <<< "<?php echo PHP_MINOR_VERSION ?>")" # Is installed php version 7.0 or greater - if [ "$(echo "$phpInsMajor.$phpInsMinor < 7.0" | bc )" == 0 ]; then + if [ "${phpInsMajor}" -ge 7 ]; then phpInsNewer=true fi fi @@ -225,9 +236,9 @@ if command -v apt-get &> /dev/null; then } # If apt-get is not found, check for rpm to see if it's a Red Hat family OS -elif command -v rpm &> /dev/null; then +elif is_command rpm ; then # Then check if dnf or yum is the package manager - if command -v dnf &> /dev/null; then + if is_command dnf ; then PKG_MANAGER="dnf" else PKG_MANAGER="yum" @@ -237,28 +248,28 @@ elif command -v rpm &> /dev/null; then UPDATE_PKG_CACHE=":" PKG_INSTALL=(${PKG_MANAGER} install -y) PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l" - INSTALLER_DEPS=(dialog git iproute net-tools newt procps-ng which) + INSTALLER_DEPS=(dialog git iproute newt procps-ng which) PIHOLE_DEPS=(bc bind-utils cronie curl findutils nmap-ncat sudo unzip wget libidn2 psmisc) PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo) LIGHTTPD_USER="lighttpd" LIGHTTPD_GROUP="lighttpd" LIGHTTPD_CFG="lighttpd.conf.fedora" # If the host OS is Fedora, - if grep -qi 'fedora' /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 # ensure 'php-json' is installed on Fedora (installed as dependency on CentOS7 + Remi repository) PIHOLE_WEB_DEPS+=('php-json') # or if host OS is CentOS, - elif grep -qi 'centos' /etc/redhat-release; then + elif grep -qiE 'centos|scientific' /etc/redhat-release; then # Pi-Hole currently supports CentOS 7+ with PHP7+ SUPPORTED_CENTOS_VERSION=7 SUPPORTED_CENTOS_PHP_VERSION=7 # Check current CentOS major release version - CURRENT_CENTOS_VERSION=$(rpm -q --queryformat '%{VERSION}' centos-release) + CURRENT_CENTOS_VERSION=$(grep -oP '(?<= )[0-9]+(?=\.)' /etc/redhat-release) # Check if CentOS version is supported if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then - echo -e " ${CROSS} CentOS $CURRENT_CENTOS_VERSION is not suported." - echo -e " Please update to CentOS release $SUPPORTED_CENTOS_VERSION or later" + printf " %b CentOS %s is not supported.\\n" "${CROSS}" "${CURRENT_CENTOS_VERSION}" + printf " Please update to CentOS release %s or later.\\n" "${SUPPORTED_CENTOS_VERSION}" # exit the installer exit fi @@ -266,9 +277,9 @@ elif command -v rpm &> /dev/null; then EPEL_PKG="epel-release" rpm -q ${EPEL_PKG} &> /dev/null || rc=$? if [[ $rc -ne 0 ]]; then - echo -e " ${INFO} Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)" + printf " %b Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)\\n" "${INFO}" "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null - echo -e " ${TICK} Installed ${EPEL_PKG}" + printf " %b Installed %s\\n" "${TICK}" "${EPEL_PKG}" fi # The default php on CentOS 7.x is 5.4 which is EOL @@ -285,42 +296,45 @@ elif command -v rpm &> /dev/null; 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 - echo -e " ${INFO} User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use." + 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 - echo -e " ${INFO} Enabling Remi's RPM repository (https://rpms.remirepo.net)" + 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 - echo -e " ${TICK} Remi's RPM repository has been enabled for PHP7" + 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 - echo -e " ${TICK} PHP7 installed/updated via Remi's RPM repository" + printf " %b PHP7 installed/updated via Remi's RPM repository\\n" "${TICK}" else - echo -e " ${CROSS} There was a problem updating to PHP7 via Remi's RPM repository" + printf " %b There was a problem updating to PHP7 via Remi's RPM repository\\n" "${CROSS}" exit 1 fi fi fi fi else - # If not a supported version of Fedora or CentOS, - echo -e " ${CROSS} Unsupported RPM based distribution" - # exit the installer - exit + # Warn user of unsupported version of Fedora or CentOS + if ! whiptail --defaultno --title "Unsupported RPM based distribution" --yesno "Would you like to continue installation on an unsupported RPM based distribution?\\n\\nPlease ensure the following packages have been installed manually:\\n\\n- lighttpd\\n- lighttpd-fastcgi\\n- PHP version 7+" ${r} ${c}; then + printf " %b Aborting installation due to unsupported RPM based distribution\\n" "${CROSS}" + exit # exit the installer + else + printf " %b Continuing installation with unsupported RPM based distribution\\n" "${INFO}" + fi fi -# If neither apt-get or rmp/dnf are found +# If neither apt-get or yum/dnf package managers were found else # it's not an OS we can support, - echo -e " ${CROSS} OS distribution not supported" + printf " %b OS distribution not supported\\n" "${CROSS}" # so exit the installer exit fi } -# A function for checking if a folder is a git repository +# A function for checking if a directory is a git repository is_repo() { # Use a named, local variable instead of the vague $1, which is the first argument passed to this function # These local variables should always be lowercase @@ -335,7 +349,7 @@ is_repo() { if [[ -d "${directory}" ]]; then # move into the directory cd "${directory}" - # Use git to check if the folder is a repo + # Use git to check if the directory is a repo # git -C is not used here to support git versions older than 1.8.4 git status --short &> /dev/null || rc=$? # If the command was not successful, @@ -357,7 +371,7 @@ make_repo() { # The message to display when this function is running str="Clone ${remoteRepo} into ${directory}" # Display the message and use the color table to preface the message with an "info" indicator - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If the directory exists, if [[ -d "${directory}" ]]; then # delete everything in it so git can clone into it @@ -366,7 +380,7 @@ make_repo() { # Clone the repo and return the return code from this command git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null || return $? # Show a colored message showing it's status - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Always return 0? Not sure this is correct return 0 } @@ -390,14 +404,14 @@ update_repo() { # Move into the directory that was passed as an argument cd "${directory}" &> /dev/null || return 1 # Let the user know what's happening - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # Stash any local commits as they conflict with our working code git stash --all --quiet &> /dev/null || true # Okay for stash failure git clean --quiet --force -d || true # Okay for already clean directory # Pull the latest commits git pull --quiet &> /dev/null || return $? # Show a completion message - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Move back into the original directory cd "${curdir}" &> /dev/null || return 1 return 0 @@ -413,19 +427,19 @@ getGitFiles() { # A local variable containing the message to be displayed local str="Check for existing repository in ${1}" # Show the message - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # Check if the directory is a repository if is_repo "${directory}"; then # Show that we're checking it - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Update the repo, returning an error message on failure - update_repo "${directory}" || { echo -e "\\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; } + update_repo "${directory}" || { printf "\\n %b: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } # If it's not a .git repo, else # Show an error - echo -e "${OVER} ${CROSS} ${str}" + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" # Attempt to make the repository, showing an error on failure - make_repo "${directory}" "${remoteRepo}" || { echo -e "\\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; } + make_repo "${directory}" "${remoteRepo}" || { printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } fi # echo a blank line echo "" @@ -442,30 +456,41 @@ resetRepo() { # Store the message in a variable str="Resetting repository within ${1}..." # Show the message - echo -ne " ${INFO} ${str}" + printf " %b %s..." "${INFO}" "${str}" # Use git to remove the local changes git reset --hard &> /dev/null || return $? # And show the status - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Returning success anyway? return 0 } -# We need to know the IPv4 information so we can effectively setup the DNS server -# Without this information, we won't know where to Pi-hole will be found find_IPv4_information() { + # Detects IPv4 address used for communication to WAN addresses. + # Accepts no arguments, returns no values. + # Named, local variables local route + local IPv4bare + # Find IP used to route to outside world by checking the the route to Google's public DNS server route=$(ip route get 8.8.8.8) - # Use awk to strip out just the interface device as it is used in future commands - IPv4dev=$(awk '{for (i=1; i<=NF; i++) if ($i~/dev/) print $(i+1)}' <<< "${route}") - # Get just the IP address - IPv4bare=$(awk '{print $7}' <<< "${route}") - # Append the CIDR notation to the IP address - IPV4_ADDRESS=$(ip -o -f inet addr show | grep "${IPv4bare}" | awk '{print $4}' | awk 'END {print}') - # Get the default gateway (the way to reach the Internet) - IPv4gw=$(awk '{print $3}' <<< "${route}") + + # Get just the interface IPv4 address + # shellcheck disable=SC2059,SC2086 + # disabled as we intentionally want to split on whitespace and have printf populate + # the variable with just the first field. + printf -v IPv4bare "$(printf ${route#*src })" + # Get the default gateway IPv4 address (the way to reach the Internet) + # shellcheck disable=SC2059,SC2086 + printf -v IPv4gw "$(printf ${route#*via })" + + if ! valid_ip "${IPv4bare}" ; then + IPv4bare="127.0.0.1" + fi + + # Append the CIDR notation to the IP address, if valid_ip fails this should return 127.0.0.1/8 + IPV4_ADDRESS=$(ip -oneline -family inet address show | grep "${IPv4bare}" | awk '{print $4}' | awk 'END {print}') } # Get available interfaces that are UP @@ -502,36 +527,36 @@ verifyFreeDiskSpace() { # If the existing space is not an integer, if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then # show an error that we can't determine the free space - echo -e " ${CROSS} ${str}" - echo -e " ${INFO} Unknown free disk space!" - echo -e " ${INFO} We were unable to determine available free disk space on this system." - echo -e " ${INFO} You may override this check, however, it is not recommended" - echo -e " ${INFO} The option '${COL_LIGHT_RED}--i_do_not_follow_recommendations${COL_NC}' can override this" - echo -e " ${INFO} e.g: curl -L https://install.pi-hole.net | bash /dev/stdin ${COL_LIGHT_RED}<option>${COL_NC}" + printf " %b %s\\n" "${CROSS}" "${str}" + printf " %b Unknown free disk space! \\n" "${INFO}" + printf " We were unable to determine available free disk space on this system.\\n" + printf " You may override this check, however, it is not recommended.\\n" + printf " The option '%b--i_do_not_follow_recommendations%b' can override this.\\n" "${COL_LIGHT_RED}" "${COL_NC}" + printf " e.g: curl -L https://install.pi-hole.net | bash /dev/stdin %b<option>%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" # exit with an error code exit 1 # If there is insufficient free disk space, elif [[ "${existing_free_kilobytes}" -lt "${required_free_kilobytes}" ]]; then # show an error message - echo -e " ${CROSS} ${str}" - echo -e " ${INFO} Your system disk appears to only have ${existing_free_kilobytes} KB free" - echo -e " ${INFO} It is recommended to have a minimum of ${required_free_kilobytes} KB to run the Pi-hole" + printf " %b %s\\n" "${CROSS}" "${str}" + printf " %b Your system disk appears to only have %s KB free\\n" "${INFO}" "${existing_free_kilobytes}" + printf " It is recommended to have a minimum of %s KB to run the Pi-hole\\n" "${required_free_kilobytes}" # if the vcgencmd command exists, - if command -v vcgencmd &> /dev/null; then + if is_command vcgencmd ; then # it's probably a Raspbian install, so show a message about expanding the filesystem - echo -e " ${INFO} If this is a new install you may need to expand your disk" - echo -e " ${INFO} Run 'sudo raspi-config', and choose the 'expand file system' option" - echo -e " ${INFO} After rebooting, run this installation again" - echo -e " ${INFO} e.g: curl -L https://install.pi-hole.net | bash" + printf " If this is a new install you may need to expand your disk\\n" + printf " Run 'sudo raspi-config', and choose the 'expand file system' option\\n" + printf " After rebooting, run this installation again\\n" + printf " e.g: curl -L https://install.pi-hole.net | bash\\n" fi # Show there is not enough free space - echo -e "\\n ${COL_LIGHT_RED}Insufficient free space, exiting...${COL_NC}" + printf "\\n %bInsufficient free space, exiting...%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" # and exit with an error exit 1 # Otherwise, else # Show that we're running a disk space check - echo -e " ${TICK} ${str}" + printf " %b %s\\n" "${TICK}" "${str}" fi } @@ -549,7 +574,7 @@ chooseInterface() { local firstLoop=1 # Find out how many interfaces are available to choose from - interfaceCount=$(echo "${availableInterfaces}" | wc -l) + interfaceCount=$(wc -l <<< "${availableInterfaces}") # If there is one interface, if [[ "${interfaceCount}" -eq 1 ]]; then @@ -576,13 +601,13 @@ chooseInterface() { # Now run the command using the interfaces saved into the array chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \ # If the user chooses Cancel, exit - { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } + { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } # For each interface for desiredInterface in ${chooseInterfaceOptions}; do # Set the one the user selected as the interface to use PIHOLE_INTERFACE=${desiredInterface} # and show this information to the user - echo -e " ${INFO} Using interface: $PIHOLE_INTERFACE" + printf " %b Using interface: %s\\n" "${INFO}" "${PIHOLE_INTERFACE}" done fi } @@ -592,21 +617,24 @@ chooseInterface() { # See https://github.com/pi-hole/pi-hole/issues/1473#issuecomment-301745953 testIPv6() { # first will contain fda2 (ULA) - first="$(cut -f1 -d":" <<< "$1")" + printf -v first "%s" "${1%%:*}" # value1 will contain 253 which is the decimal value corresponding to 0xfd value1=$(( (0x$first)/256 )) # will contain 162 which is the decimal value corresponding to 0xa2 value2=$(( (0x$first)%256 )) # the ULA test is testing for fc00::/7 according to RFC 4193 if (( (value1&254)==252 )); then + # echoing result to calling function as return value echo "ULA" fi # the GUA test is testing for 2000::/3 according to RFC 4291 if (( (value1&112)==32 )); then + # echoing result to calling function as return value echo "GUA" fi # the LL test is testing for fe80::/10 according to RFC 4193 if (( (value1)==254 )) && (( (value2&192)==128 )); then + # echoing result to calling function as return value echo "Link-local" fi } @@ -632,17 +660,17 @@ useIPv6dialog() { # set the IPv6 address to the ULA address IPV6_ADDRESS="${ULA_ADDRESS}" # Show this info to the user - echo -e " ${INFO} Found IPv6 ULA address, using it for blocking IPv6 ads" + printf " %b Found IPv6 ULA address, using it for blocking IPv6 ads\\n" "${INFO}" # Otherwise, if the GUA_ADDRESS has a value, elif [[ ! -z "${GUA_ADDRESS}" ]]; then # Let the user know - echo -e " ${INFO} Found IPv6 GUA address, using it for blocking IPv6 ads" + printf " %b Found IPv6 GUA address, using it for blocking IPv6 ads\\n" "${INFO}" # And assign it to the global variable IPV6_ADDRESS="${GUA_ADDRESS}" # If none of those work, else # explain that IPv6 blocking will not be used - echo -e " ${INFO} Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled" + printf " %b Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled\\n" "${INFO}" # So set the variable to be empty IPV6_ADDRESS="" fi @@ -667,7 +695,7 @@ use4andor6() { # or IPv6 (on by default if available) IPv6 "Block ads over IPv6" on) # In a variable, show the choices available; exit if Cancel is selected - choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } + choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } # For each choice available, for choice in ${choices} do @@ -690,12 +718,12 @@ use4andor6() { useIPv6dialog fi # Echo the information to the user - echo -e " ${INFO} IPv4 address: ${IPV4_ADDRESS}" - echo -e " ${INFO} IPv6 address: ${IPV6_ADDRESS}" + printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" + printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}" # If neither protocol is selected, if [[ ! "${useIPv4}" ]] && [[ ! "${useIPv6}" ]]; then # Show an error in red - echo -e " ${COL_LIGHT_RED}Error: Neither IPv4 or IPv6 selected${COL_NC}" + printf " %bError: Neither IPv4 or IPv6 selected%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" # and exit with an error exit 1 fi @@ -725,13 +753,13 @@ It is also possible to use a DHCP reservation, but if you are going to do that, IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \ # Cancelling IPv4 settings window { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } - echo -e " ${INFO} Your static IPv4 address: ${IPV4_ADDRESS}" + printf " %b Your static IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" # Ask for the gateway IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) || \ # Cancelling gateway settings window { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } - echo -e " ${INFO} Your static IPv4 gateway: ${IPv4gw}" + printf " %b Your static IPv4 gateway: %s\\n" "${INFO}" "${IPv4gw}" # Give the user a chance to review their settings before moving on if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct? @@ -748,79 +776,98 @@ It is also possible to use a DHCP reservation, but if you are going to do that, fi } -# dhcpcd is very annoying, +# configure networking via dhcpcd setDHCPCD() { - # but we can append these lines to dhcpcd.conf to enable a static IP - echo "interface ${PIHOLE_INTERFACE} - static ip_address=${IPV4_ADDRESS} - static routers=${IPv4gw} - static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null + # check if the IP is already in the file + if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then + printf " %b Static IP already configured\\n" "${INFO}" + # If it's not, + else + # we can append these lines to dhcpcd.conf to enable a static IP + echo "interface ${PIHOLE_INTERFACE} + static ip_address=${IPV4_ADDRESS} + static routers=${IPv4gw} + static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null + # Then use the ip command to immediately set the new address + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + # Also give a warning that the user may need to reboot their system + printf " %b Set IP address to %s \\n You may need to restart after the install is complete\\n" "${TICK}" "${IPV4_ADDRESS%/*}" + fi } -setStaticIPv4() { +# configure networking ifcfg-xxxx file found at /etc/sysconfig/network-scripts/ +# this function requires the full path of an ifcfg file passed as an argument +setIFCFG() { # Local, named variables local IFCFG_FILE local IPADDR local CIDR + IFCFG_FILE=$1 + printf -v IPADDR "%s" "${IPV4_ADDRESS%%/*}" + # check if the desired IP is already set + if grep -Eq "${IPADDR}(\\b|\\/)" "${IFCFG_FILE}"; then + printf " %b Static IP already configured\\n" "${INFO}" + # Otherwise, + else + # Put the IP in variables without the CIDR notation + printf -v CIDR "%s" "${IPV4_ADDRESS##*/}" + # Backup existing interface configuration: + cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig + # Build Interface configuration file using the GLOBAL variables we have + { + echo "# Configured via Pi-hole installer" + echo "DEVICE=$PIHOLE_INTERFACE" + echo "BOOTPROTO=none" + echo "ONBOOT=yes" + echo "IPADDR=$IPADDR" + echo "PREFIX=$CIDR" + echo "GATEWAY=$IPv4gw" + echo "DNS1=$PIHOLE_DNS_1" + echo "DNS2=$PIHOLE_DNS_2" + echo "USERCTL=no" + }> "${IFCFG_FILE}" + # Use ip to immediately set the new address + ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" + # If NetworkMangler command line interface exists and ready to mangle, + if is_command nmcli && nmcli general status &> /dev/null; then + # Tell NetworkManagler to read our new sysconfig file + nmcli con load "${IFCFG_FILE}" > /dev/null + fi + # Show a warning that the user may need to restart + printf " %b Set IP address to %s\\n You may need to restart after the install is complete\\n" "${TICK}" "${IPV4_ADDRESS%%/*}" + fi +} + +setStaticIPv4() { + # Local, named variables + local IFCFG_FILE + local CONNECTION_NAME # For the Debian family, if dhcpcd.conf exists, if [[ -f "/etc/dhcpcd.conf" ]]; then - # check if the IP is already in the file - if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then - echo -e " ${INFO} Static IP already configured" - # If it's not, - else - # set it using our function - setDHCPCD - # Then use the ip command to immediately set the new address - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - # Also give a warning that the user may need to reboot their system - echo -e " ${TICK} Set IP address to ${IPV4_ADDRESS%/*} - You may need to restart after the install is complete" - fi - # If it's not Debian, check if it's the Fedora family by checking for the file below - elif [[ -f "/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}" ]];then + # configure networking via dhcpcd + setDHCPCD + return 0 + fi + # If a DHCPCD config file was not found, check for an ifcfg config file based on interface name + if [[ -f "/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}" ]];then # If it exists, IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} - IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/) - # check if the desired IP is already set - if grep -Eq "${IPADDR}(\\b|\\/)" "${IFCFG_FILE}"; then - echo -e " ${INFO} Static IP already configured" - # Otherwise, - else - # Put the IP in variables without the CIDR notation - CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/) - # Backup existing interface configuration: - cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig - # Build Interface configuration file using the GLOBAL variables we have - { - echo "# Configured via Pi-hole installer" - echo "DEVICE=$PIHOLE_INTERFACE" - echo "BOOTPROTO=none" - echo "ONBOOT=yes" - echo "IPADDR=$IPADDR" - echo "PREFIX=$CIDR" - echo "GATEWAY=$IPv4gw" - echo "DNS1=$PIHOLE_DNS_1" - echo "DNS2=$PIHOLE_DNS_2" - echo "USERCTL=no" - }> "${IFCFG_FILE}" - # Use ip to immediately set the new address - ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" - # If NetworkMangler command line interface exists and ready to mangle, - if command -v nmcli &> /dev/null && nmcli general status &> /dev/null; then - # Tell NetworkManagler to read our new sysconfig file - nmcli con load "${IFCFG_FILE}" > /dev/null - fi - # Show a warning that the user may need to restart - echo -e " ${TICK} Set IP address to ${IPV4_ADDRESS%/*} - You may need to restart after the install is complete" + setIFCFG "${IFCFG_FILE}" + return 0 + fi + # if an ifcfg config does not exists for the interface name, try the connection name via network manager + if is_command nmcli && nmcli general status &> /dev/null; then + CONNECTION_NAME=$(nmcli dev show "${PIHOLE_INTERFACE}" | grep 'GENERAL.CONNECTION' | cut -d: -f2 | sed 's/^System//' | xargs | tr ' ' '_') + if [[ -f "/etc/sysconfig/network-scripts/ifcfg-${CONNECTION_NAME}" ]];then + # If it exists, + IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${CONNECTION_NAME} + setIFCFG "${IFCFG_FILE}" + return 0 fi - # If all that fails, - else - # show an error and exit - echo -e " ${INFO} Warning: Unable to locate configuration file to set static IPv4 address" - exit 1 fi + # If previous conditions failed, show an error and exit + printf " %b Warning: Unable to locate configuration file to set static IPv4 address\\n" "${INFO}" + exit 1 } # Check an IP address to see if it is a valid one @@ -858,7 +905,6 @@ setDNS() { DNSChooseOptions=(Google "" OpenDNS "" Level3 "" - Norton "" Comodo "" DNSWatch "" Quad9 "" @@ -869,54 +915,49 @@ setDNS() { DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 7 \ "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \ # exit if Cancel is selected - { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } + { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } # Display the selection - echo -ne " ${INFO} Using " + printf " %b Using " "${INFO}" # Depending on the user's choice, set the GLOBAl variables to the IP of the respective provider case ${DNSchoices} in Google) - echo "Google DNS servers" + printf "Google DNS servers\\n" PIHOLE_DNS_1="8.8.8.8" PIHOLE_DNS_2="8.8.4.4" ;; OpenDNS) - echo "OpenDNS servers" + printf "OpenDNS servers\\n" PIHOLE_DNS_1="208.67.222.222" PIHOLE_DNS_2="208.67.220.220" ;; Level3) - echo "Level3 servers" + printf "Level3 servers\\n" PIHOLE_DNS_1="4.2.2.1" PIHOLE_DNS_2="4.2.2.2" ;; - Norton) - echo "Norton ConnectSafe servers" - PIHOLE_DNS_1="199.85.126.10" - PIHOLE_DNS_2="199.85.127.10" - ;; Comodo) - echo "Comodo Secure servers" + printf "Comodo Secure servers\\n" PIHOLE_DNS_1="8.26.56.26" PIHOLE_DNS_2="8.20.247.20" ;; DNSWatch) - echo "DNS.WATCH servers" + printf "DNS.WATCH servers\\n" PIHOLE_DNS_1="84.200.69.80" PIHOLE_DNS_2="84.200.70.40" ;; Quad9) - echo "Quad9 servers" + printf "Quad9 servers\\n" PIHOLE_DNS_1="9.9.9.9" PIHOLE_DNS_2="149.112.112.112" ;; FamilyShield) - echo "FamilyShield servers" + printf "FamilyShield servers\\n" PIHOLE_DNS_1="208.67.222.123" PIHOLE_DNS_2="208.67.220.123" ;; Cloudflare) - echo "Cloudflare servers" + printf "Cloudflare servers\\n" PIHOLE_DNS_1="1.1.1.1" PIHOLE_DNS_2="1.0.0.1" ;; @@ -942,10 +983,13 @@ setDNS() { # Dialog for the user to enter custom upstream servers piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) || \ - { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } - # - PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') - PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') + { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } + # Clean user input and replace whitespace with comma. + piholeDNS=$(sed 's/[, \t]\+/,/g' <<< "${piholeDNS}") + + printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}" + printf -v PIHOLE_DNS_2 "%s" "${piholeDNS##*,}" + # If the IP is valid, if ! valid_ip "${PIHOLE_DNS_1}" || [[ ! "${PIHOLE_DNS_1}" ]]; then # store it in the variable so we can use it @@ -998,23 +1042,45 @@ setLogging() { LogChooseOptions=("On (Recommended)" "" on Off "" off) # Get the user's choice - LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1) + LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1) case ${LogChoices} in # If it's on "On (Recommended)") - echo -e " ${INFO} Logging On." + printf " %b Logging On.\\n" "${INFO}" # Set the GLOBAL variable to true so we know what they selected QUERY_LOGGING=true ;; # Otherwise, it's off, Off) - echo -e " ${INFO} Logging Off." + printf " %b Logging Off.\\n" "${INFO}" # So set it to false QUERY_LOGGING=false ;; esac } +# Allow the user to set their FTL privacy level +setPrivacyLevel() { + local LevelCommand + local LevelOptions + + LevelCommand=(whiptail --separate-output --radiolist "Select a privacy mode for FTL." "${r}" "${c}" 6) + + # The default selection is level 0 + LevelOptions=( + "0" "Show everything" on + "1" "Hide domains" off + "2" "Hide domains and clients" off + "3" "Anonymous mode" off + "4" "Disabled statistics" off + ) + + # Get the user's choice + PRIVACY_LEVEL=$("${LevelCommand[@]}" "${LevelOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1) + + printf " %b Privacy level %d" "${INFO}" "${PRIVACY_LEVEL}" +} + # Function to ask the user if they want to install the dashboard setAdminFlag() { # Local, named variables @@ -1027,16 +1093,16 @@ setAdminFlag() { # with the default being enabled WebChooseOptions=("On (Recommended)" "" on Off "" off) - WebChoices=$("${WebToggleCommand[@]}" "${WebChooseOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1) + WebChoices=$("${WebToggleCommand[@]}" "${WebChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1) # Depending on their choice case ${WebChoices} in "On (Recommended)") - echo -e " ${INFO} Web Interface On" + printf " %b Web Interface On\\n" "${INFO}" # Set it to true INSTALL_WEB_INTERFACE=true ;; Off) - echo -e " ${INFO} Web Interface Off" + printf " %b Web Interface Off\\n" "${INFO}" # or false INSTALL_WEB_INTERFACE=false ;; @@ -1048,16 +1114,16 @@ setAdminFlag() { # with the default being enabled WebChooseOptions=("On (Recommended)" "" on Off "" off) - WebChoices=$("${WebToggleCommand[@]}" "${WebChooseOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1) + WebChoices=$("${WebToggleCommand[@]}" "${WebChooseOptions[@]}" 2>&1 >/dev/tty) || (printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" && exit 1) # Depending on their choice case ${WebChoices} in "On (Recommended)") - echo -e " ${INFO} Web Server On" + printf " %b Web Server On\\n" "${INFO}" # set it to true, as clearly seen below. INSTALL_WEB_SERVER=true ;; Off) - echo -e " ${INFO} Web Server Off" + printf " %b Web Server Off\\n" "${INFO}" # or false INSTALL_WEB_SERVER=false ;; @@ -1083,23 +1149,46 @@ chooseBlocklists() { HostsFile "Hosts-file.net Ads" on) # In a variable, show the choices available; exit if Cancel is selected - choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; rm "${adlistFile}" ;exit 1; } + choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; rm "${adlistFile}" ;exit 1; } # For each choice available, for choice in ${choices} do - # Set the values to true - case ${choice} in - StevenBlack ) echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}";; - MalwareDom ) echo "https://mirror1.malwaredomains.com/files/justdomains" >> "${adlistFile}";; - Cameleon ) echo "http://sysctl.org/cameleon/hosts" >> "${adlistFile}";; - ZeusTracker ) echo "https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist" >> "${adlistFile}";; - DisconTrack ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" >> "${adlistFile}";; - DisconAd ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt" >> "${adlistFile}";; - HostsFile ) echo "https://hosts-file.net/ad_servers.txt" >> "${adlistFile}";; - esac + appendToListsFile "${choice}" done } +# Accept a string parameter, it must be one of the default lists +# This function allow to not duplicate code in chooseBlocklists and +# in installDefaultBlocklists +appendToListsFile() { + case $1 in + StevenBlack ) echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}";; + MalwareDom ) echo "https://mirror1.malwaredomains.com/files/justdomains" >> "${adlistFile}";; + Cameleon ) echo "http://sysctl.org/cameleon/hosts" >> "${adlistFile}";; + ZeusTracker ) echo "https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist" >> "${adlistFile}";; + DisconTrack ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" >> "${adlistFile}";; + DisconAd ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt" >> "${adlistFile}";; + HostsFile ) echo "https://hosts-file.net/ad_servers.txt" >> "${adlistFile}";; + esac +} + +# Used only in unattended setup +# If there is already the adListFile, we keep it, else we create it using all default lists +installDefaultBlocklists() { + # In unattended setup, could be useful to use userdefined blocklist. + # If this file exists, we avoid overriding it. + if [[ -f "${adlistFile}" ]]; then + return; + fi + appendToListsFile StevenBlack + appendToListsFile MalwareDom + appendToListsFile Cameleon + appendToListsFile ZeusTracker + appendToListsFile DisconTrack + appendToListsFile DisconAd + appendToListsFile HostsFile +} + # Check if /etc/dnsmasq.conf is from pi-hole. If so replace with an original and install new in .d directory version_check_dnsmasq() { # Local, named variables @@ -1112,39 +1201,39 @@ version_check_dnsmasq() { # If the dnsmasq config file exists if [[ -f "${dnsmasq_conf}" ]]; then - echo -ne " ${INFO} Existing dnsmasq.conf found..." + printf " %b Existing dnsmasq.conf found..." "${INFO}" # If gravity.list is found within this file, we presume it's from older versions on Pi-hole, if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then - echo " it is from a previous Pi-hole install." - echo -ne " ${INFO} Backing up dnsmasq.conf to dnsmasq.conf.orig..." + printf " it is from a previous Pi-hole install.\\n" + printf " %b Backing up dnsmasq.conf to dnsmasq.conf.orig..." "${INFO}" # so backup the original file mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig} - echo -e "${OVER} ${TICK} Backing up dnsmasq.conf to dnsmasq.conf.orig..." - echo -ne " ${INFO} Restoring default dnsmasq.conf..." + printf "%b %b Backing up dnsmasq.conf to dnsmasq.conf.orig...\\n" "${OVER}" "${TICK}" + printf " %b Restoring default dnsmasq.conf..." "${INFO}" # and replace it with the default cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo -e "${OVER} ${TICK} Restoring default dnsmasq.conf..." + printf "%b %b Restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}" # Otherwise, else # Don't to anything - echo " it is not a Pi-hole file, leaving alone!" + printf " it is not a Pi-hole file, leaving alone!\\n" fi else # If a file cannot be found, - echo -ne " ${INFO} No dnsmasq.conf found... restoring default dnsmasq.conf..." + printf " %b No dnsmasq.conf found... restoring default dnsmasq.conf..." "${INFO}" # restore the default one cp ${dnsmasq_original_config} ${dnsmasq_conf} - echo -e "${OVER} ${TICK} No dnsmasq.conf found... restoring default dnsmasq.conf..." + printf "%b %b No dnsmasq.conf found... restoring default dnsmasq.conf...\\n" "${OVER}" "${TICK}" fi - echo -en " ${INFO} Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." + printf " %b Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..." "${INFO}" # Check to see if dnsmasq directory exists (it may not due to being a fresh install and dnsmasq no longer being a dependency) if [[ ! -d "/etc/dnsmasq.d" ]];then mkdir "/etc/dnsmasq.d" fi # Copy the new Pi-hole DNS config file into the dnsmasq.d directory cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location} - echo -e "${OVER} ${TICK} Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf" + printf "%b %b Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf\\n" "${OVER}" "${TICK}" # Replace our placeholder values with the GLOBAL DNS variables that we populated earlier # First, swap in the interface to listen on sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location} @@ -1198,7 +1287,7 @@ clean_existing() { installScripts() { # Local, named variables local str="Installing scripts from ${PI_HOLE_LOCAL_REPO}" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # Clear out script files from Pi-hole scripts directory. clean_existing "${PI_HOLE_INSTALL_DIR}" "${PI_HOLE_FILES[@]}" @@ -1220,27 +1309,26 @@ installScripts() { install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./advanced/Scripts/COL_TABLE install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Otherwise, else # Show an error and exit - echo -e "${OVER} ${CROSS} ${str} - ${COL_LIGHT_RED}Error: Local repo ${PI_HOLE_LOCAL_REPO} not found, exiting installer${COL_NC}" + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + printf "\\t\\t%bError: Local repo %s not found, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}" return 1 fi } # Install the configs from PI_HOLE_LOCAL_REPO to their various locations installConfigs() { - echo "" - echo -e " ${INFO} Installing configs from ${PI_HOLE_LOCAL_REPO}..." + printf "\\n %b Installing configs from %s...\\n" "${INFO}" "${PI_HOLE_LOCAL_REPO}" # Make sure Pi-hole's config files are in place version_check_dnsmasq # Install empty file if it does not exist if [[ ! -f "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then - if ! install -o pihole -g pihole -m 664 /dev/null "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" &>/dev/nul; then - echo -e " ${COL_LIGHT_RED}Error: Unable to initialize configuration file ${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" + if ! install -o pihole -g pihole -m 664 /dev/null "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" &>/dev/null; then + printf " %bError: Unable to initialize configuration file %s/pihole-FTL.conf\\n" "${COL_LIGHT_RED}" "${PI_HOLE_CONFIG_DIR}" return 1 fi fi @@ -1264,6 +1352,8 @@ installConfigs() { fi # and copy in the config file Pi-hole needs cp ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf + # Make sure the external.conf file exists, as lighttpd v1.4.50 crashes without it + touch /etc/lighttpd/external.conf # if there is a custom block page in the html/pihole directory, replace 404 handler in lighttpd config if [[ -f "/var/www/html/pihole/custom.php" ]]; then sed -i 's/^\(server\.error-handler-404\s*=\s*\).*$/\1"pihole\/custom\.php"/' /etc/lighttpd/lighttpd.conf @@ -1282,14 +1372,14 @@ install_manpage() { # Copy Pi-hole man pages and call mandb to update man page database # Default location for man files for /usr/local/bin is /usr/local/share/man # on lightweight systems may not be present, so check before copying. - echo -en " ${INFO} Testing man page installation" - if ! command -v mandb &>/dev/null; then + printf " %b Testing man page installation" "${INFO}" + if ! is_command mandb ; then # if mandb is not present, no manpage support - echo -e "${OVER} ${INFO} man not installed" + printf "%b %b man not installed\\n" "${OVER}" "${INFO}" return elif [[ ! -d "/usr/local/share/man" ]]; then # appropriate directory for Pi-hole's man page is not present - echo -e "${OVER} ${INFO} man pages not installed" + printf "%b %b man pages not installed\\n" "${OVER}" "${INFO}" return fi if [[ ! -d "/usr/local/share/man/man8" ]]; then @@ -1306,13 +1396,13 @@ install_manpage() { cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.conf.5 /usr/local/share/man/man5/pihole-FTL.conf.5 if mandb -q &>/dev/null; then # Updated successfully - echo -e "${OVER} ${TICK} man pages installed and database updated" + printf "%b %b man pages installed and database updated\\n" "${OVER}" "${TICK}" return else # Something is wrong with the system's man installation, clean up # our files, (leave everything how we found it). rm /usr/local/share/man/man8/pihole.8 /usr/local/share/man/man8/pihole-FTL.8 /usr/local/share/man/man5/pihole-FTL.conf.5 - echo -e "${OVER} ${CROSS} man page db not updated, man pages not installed" + printf "%b %b man page db not updated, man pages not installed\\n" "${OVER}" "${CROSS}" fi } @@ -1320,22 +1410,22 @@ stop_service() { # Stop service passed in as argument. # Can softfail, as process may not be installed when this is called local str="Stopping ${1} service" - echo -ne " ${INFO} ${str}..." - if command -v systemctl &> /dev/null; then + printf " %b %s..." "${INFO}" "${str}" + if is_command systemctl ; then systemctl stop "${1}" &> /dev/null || true else service "${1}" stop &> /dev/null || true fi - echo -e "${OVER} ${TICK} ${str}..." + printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } # Start/Restart service passed in as argument start_service() { # Local, named variables local str="Starting ${1} service" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If systemctl exists, - if command -v systemctl &> /dev/null; then + if is_command systemctl ; then # use that to restart the service systemctl restart "${1}" &> /dev/null # Otherwise, @@ -1343,16 +1433,16 @@ start_service() { # fall back to the service command service "${1}" restart &> /dev/null fi - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } # Enable service so that it will start with next reboot enable_service() { # Local, named variables local str="Enabling ${1} service to start on reboot" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If systemctl exists, - if command -v systemctl &> /dev/null; then + if is_command systemctl ; then # use that to enable the service systemctl enable "${1}" &> /dev/null # Otherwise, @@ -1360,16 +1450,16 @@ enable_service() { # use update-rc.d to accomplish this update-rc.d "${1}" defaults &> /dev/null fi - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } # Disable service so that it will not with next reboot disable_service() { # Local, named variables local str="Disabling ${1} service" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If systemctl exists, - if command -v systemctl &> /dev/null; then + if is_command systemctl ; then # use that to disable the service systemctl disable "${1}" &> /dev/null # Otherwise, @@ -1377,12 +1467,12 @@ disable_service() { # use update-rc.d to accomplish this update-rc.d "${1}" disable &> /dev/null fi - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" } check_service_active() { # If systemctl exists, - if command -v systemctl &> /dev/null; then + if is_command systemctl ; then # use that to check the status of the service systemctl is-enabled "${1}" &> /dev/null # Otherwise, @@ -1394,25 +1484,25 @@ check_service_active() { # Systemd-resolved's DNSStubListener and dnsmasq can't share port 53. disable_resolved_stublistener() { - echo -en " ${INFO} Testing if systemd-resolved is enabled" + printf " %b Testing if systemd-resolved is enabled\\n" "${INFO}" # Check if Systemd-resolved's DNSStubListener is enabled and active on port 53 if check_service_active "systemd-resolved"; then # Check if DNSStubListener is enabled - echo -en " ${OVER} ${INFO} Testing if systemd-resolved DNSStub-Listener is active" + printf " %b %b Testing if systemd-resolved DNSStub-Listener is active" "${OVER}" "${INFO}" if ( grep -E '#?DNSStubListener=yes' /etc/systemd/resolved.conf &> /dev/null ); then # Disable the DNSStubListener to unbind it from port 53 # Note that this breaks dns functionality on host until dnsmasq/ftl are up and running - echo -en "${OVER} ${TICK} Disabling systemd-resolved DNSStubListener" + printf "%b %b Disabling systemd-resolved DNSStubListener" "${OVER}" "${TICK}" # Make a backup of the original /etc/systemd/resolved.conf # (This will need to be restored on uninstallation) sed -r -i.orig 's/#?DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf - echo -e " and restarting systemd-resolved" + printf " and restarting systemd-resolved\\n" systemctl reload-or-restart systemd-resolved else - echo -e "${OVER} ${INFO} Systemd-resolved does not need to be restarted" + printf "%b %b Systemd-resolved does not need to be restarted\\n" "${OVER}" "${INFO}" fi else - echo -e "${OVER} ${INFO} Systemd-resolved is not enabled" + printf "%b %b Systemd-resolved is not enabled\\n" "${OVER}" "${INFO}" fi } @@ -1425,16 +1515,15 @@ update_package_cache() { # Local, named variables local str="Update local cache of available packages" - echo "" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # Create a command from the package cache variable if eval "${UPDATE_PKG_CACHE}" &> /dev/null; then - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Otherwise, else # show an error and exit - echo -e "${OVER} ${CROSS} ${str}" - echo -ne " ${COL_LIGHT_RED}Error: Unable to update package cache. Please try \"${UPDATE_PKG_CACHE}\"${COL_NC}" + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + printf " %bError: Unable to update package cache. Please try \"%s\"%b" "${COL_LIGHT_RED}" "${COL_LIGHT_RED}" "${COL_NC}" return 1 fi } @@ -1444,22 +1533,20 @@ update_package_cache() { notify_package_updates_available() { # Local, named variables local str="Checking ${PKG_MANAGER} for upgraded packages" - echo -ne "\\n ${INFO} ${str}..." + printf "\\n %b %s..." "${INFO}" "${str}" # Store the list of packages in a variable updatesToInstall=$(eval "${PKG_COUNT}") if [[ -d "/lib/modules/$(uname -r)" ]]; then if [[ "${updatesToInstall}" -eq 0 ]]; then - echo -e "${OVER} ${TICK} ${str}... up to date!" - echo "" + printf "%b %b %s... up to date!\\n\\n" "${OVER}" "${TICK}" "${str}" else - echo -e "${OVER} ${TICK} ${str}... ${updatesToInstall} updates available" - echo -e " ${INFO} ${COL_LIGHT_GREEN}It is recommended to update your OS after installing the Pi-hole! ${COL_NC}" - echo "" + printf "%b %b %s... %s updates available\\n" "${OVER}" "${TICK}" "${str}" "${updatesToInstall}" + printf " %b %bIt is recommended to update your OS after installing the Pi-hole!%b\\n\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${COL_NC}" fi else - echo -e "${OVER} ${CROSS} ${str} - Kernel update detected. If the install fails, please reboot and try again\\n" + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + printf " Kernel update detected. If the install fails, please reboot and try again\\n" fi } @@ -1473,10 +1560,10 @@ install_dependent_packages() { # If it equals 1, if [[ "${counter}" == 1 ]]; then # - echo -e " ${INFO} Installer Dependency checks..." + printf " %b Installer Dependency checks...\\n" "${INFO}" else # - echo -e " ${INFO} Main Dependency checks..." + printf " %b Main Dependency checks...\\n" "${INFO}" fi # Install packages passed in via argument array @@ -1489,12 +1576,12 @@ install_dependent_packages() { # amount of download traffic. # NOTE: We may be able to use this installArray in the future to create a list of package that were # installed by us, and remove only the installed packages, and not the entire list. - if command -v debconf-apt-progress &> /dev/null; then + if is_command debconf-apt-progress ; then # For each package, for i in "${argArray1[@]}"; do - echo -ne " ${INFO} Checking for $i..." + printf " %b Checking for %s..." "${INFO}" "${i}" if dpkg-query -W -f='${Status}' "${i}" 2>/dev/null | grep "ok installed" &> /dev/null; then - echo -e "${OVER} ${TICK} Checking for $i" + printf "%b %b Checking for %s\\n" "${OVER}" "${TICK}" "${i}" else echo -e "${OVER} ${INFO} Checking for $i (will be installed)" installArray+=("${i}") @@ -1505,17 +1592,17 @@ install_dependent_packages() { debconf-apt-progress -- "${PKG_INSTALL[@]}" "${installArray[@]}" return fi - echo "" + printf "\\n" return 0 fi # Install Fedora/CentOS packages for i in "${argArray1[@]}"; do - echo -ne " ${INFO} Checking for $i..." + printf " %b Checking for %s..." "${INFO}" "${i}" if ${PKG_MANAGER} -q list installed "${i}" &> /dev/null; then - echo -e "${OVER} ${TICK} Checking for $i" + printf "%b %b Checking for %s" "${OVER}" "${TICK}" "${i}" else - echo -e "${OVER} ${INFO} Checking for $i (will be installed)" + printf "%b %b Checking for %s (will be installed)" "${OVER}" "${INFO}" "${i}" installArray+=("${i}") fi done @@ -1523,17 +1610,16 @@ install_dependent_packages() { "${PKG_INSTALL[@]}" "${installArray[@]}" &> /dev/null return fi - echo "" + printf "\\n" return 0 } # Install the Web interface dashboard installPiholeWeb() { - echo "" - echo " ${INFO} Installing blocking page..." + printf "\\n %b Installing blocking page...\\n" "${INFO}" local str="Creating directory for blocking page, and copying files" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # Install the directory install -d /var/www/html/pihole # and the blockpage @@ -1544,26 +1630,25 @@ installPiholeWeb() { rm "/var/www/html/pihole/index.js" fi - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" local str="Backing up index.lighttpd.html" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If the default index file exists, if [[ -f "/var/www/html/index.lighttpd.html" ]]; then # back it up mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Otherwise, else # don't do anything - echo -e "${OVER} ${CROSS} ${str} - No default index.lighttpd.html file found... not backing up" + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + printf " No default index.lighttpd.html file found... not backing up\\n" fi # Install Sudoers file - echo "" local str="Installing sudoer file" - echo -ne " ${INFO} ${str}..." + printf "\\n %b %s..." "${INFO}" "${str}" # Make the .d directory if it doesn't exist mkdir -p /etc/sudoers.d/ # and copy in the pihole sudoers file @@ -1579,22 +1664,21 @@ installPiholeWeb() { fi # Set the strict permissions on the file chmod 0440 /etc/sudoers.d/pihole - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" } # Installs a cron file installCron() { # Install the cron job local str="Installing latest Cron script" - echo "" - echo -ne " ${INFO} ${str}..." + printf "\\n %b %s..." "${INFO}" "${str}" # Copy the cron file over from the local repo cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole.cron /etc/cron.d/pihole # Randomize gravity update time sed -i "s/59 1 /$((1 + RANDOM % 58)) $((3 + RANDOM % 2))/" /etc/cron.d/pihole # Randomize update checker time sed -i "s/59 17/$((1 + RANDOM % 58)) $((12 + RANDOM % 8))/" /etc/cron.d/pihole - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" } # Gravity is a very important script as it aggregates all of the domains into a single HOSTS formatted list, @@ -1607,44 +1691,47 @@ runGravity() { # Check if the pihole user exists and create if it does not create_pihole_user() { local str="Checking for user 'pihole'" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If the user pihole exists, if id -u pihole &> /dev/null; then # just show a success - echo -ne "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # Otherwise, else - echo -ne "${OVER} ${CROSS} ${str}" + printf "%b %b %s" "${OVER}" "${CROSS}" "${str}" local str="Creating user 'pihole'" - echo -ne " ${INFO} ${str}..." + printf "%b %b %s..." "${OVER}" "${INFO}" "${str}" # create her with the useradd command - useradd -r -s /usr/sbin/nologin pihole - echo -ne "${OVER} ${TICK} ${str}" + if useradd -r -s /usr/sbin/nologin pihole; then + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" + else + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + fi fi } # Allow HTTP and DNS traffic configureFirewall() { - echo "" + printf "\\n" # If a firewall is running, if firewall-cmd --state &> /dev/null; then # ask if the user wants to install Pi-hole's default firewall rules whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ - { echo -e " ${INFO} Not installing firewall rulesets."; return 0; } - echo -e " ${TICK} Configuring FirewallD for httpd and pihole-FTL" + { printf " %b Not installing firewall rulesets.\\n" "${INFO}"; return 0; } + printf " %b Configuring FirewallD for httpd and pihole-FTL\\n" "${TICK}" # Allow HTTP and DNS traffic firewall-cmd --permanent --add-service=http --add-service=dns # Reload the firewall to apply these changes firewall-cmd --reload return 0 # Check for proper kernel modules to prevent failure - elif modinfo ip_tables &> /dev/null && command -v iptables &> /dev/null; then + elif modinfo ip_tables &> /dev/null && is_command iptables ; then # If chain Policy is not ACCEPT or last Rule is not ACCEPT # then check and insert our Rules above the DROP/REJECT Rule. if iptables -S INPUT | head -n1 | grep -qv '^-P.*ACCEPT$' || iptables -S INPUT | tail -n1 | grep -qv '^-\(A\|P\).*ACCEPT$'; then whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ - { echo -e " ${INFO} Not installing firewall rulesets."; return 0; } - echo -e " ${TICK} Installing new IPTables firewall rulesets" + { printf " %b Not installing firewall rulesets.\\n" "${INFO}"; return 0; } + printf " %b Installing new IPTables firewall rulesets\\n" "${TICK}" # Check chain first, otherwise a new rule will duplicate old ones iptables -C INPUT -p tcp -m tcp --dport 80 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT iptables -C INPUT -p tcp -m tcp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT @@ -1655,11 +1742,11 @@ configureFirewall() { # Otherwise, else # no firewall is running - echo -e " ${INFO} No active firewall detected.. skipping firewall configuration" + printf " %b No active firewall detected.. skipping firewall configuration\\n" "${INFO}" # so just exit return 0 fi - echo -e " ${INFO} Skipping firewall configuration" + printf " %b Skipping firewall configuration\\n" "${INFO}" } # @@ -1695,6 +1782,10 @@ finalExports() { echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" }>> "${setupVars}" + # Set the privacy level + sed -i '/PRIVACYLEVEL/d' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" + echo "PRIVACYLEVEL=${PRIVACY_LEVEL}" >> "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" + # Bring in the current settings and the functions to manipulate them source "${setupVars}" source "${PI_HOLE_LOCAL_REPO}/advanced/Scripts/webpage.sh" @@ -1710,8 +1801,7 @@ finalExports() { installLogrotate() { local str="Installing latest logrotate script" - echo "" - echo -ne " ${INFO} ${str}..." + printf "\\n %b %s..." "${INFO}" "${str}" # Copy the file over from the local repo cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/logrotate /etc/pihole/logrotate # Different operating systems have different user / group @@ -1726,7 +1816,7 @@ installLogrotate() { # sed -i "s/# su #/su ${logusergroup}/g;" /etc/pihole/logrotate fi - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" } # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. @@ -1769,13 +1859,13 @@ installPihole() { # Give pihole access to the Web server group usermod -a -G ${LIGHTTPD_GROUP} pihole # If the lighttpd command is executable, - if [[ -x "$(command -v lighty-enable-mod)" ]]; then + if is_command lighty-enable-mod ; then # enable fastcgi and fastcgi-php lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else # Otherwise, show info about installing them - echo -e " ${INFO} Warning: 'lighty-enable-mod' utility not found - Please ensure fastcgi is enabled if you experience issues\\n" + printf " %b Warning: 'lighty-enable-mod' utility not found\\n" "${INFO}" + printf " Please ensure fastcgi is enabled if you experience issues\\n" fi fi fi @@ -1785,12 +1875,12 @@ installPihole() { fi # Install base files and web interface if ! installScripts; then - echo -e " {CROSS} Failure in dependent script copy function." + printf " %b Failure in dependent script copy function.\\n" "${CROSS}" exit 1 fi # Install config files if ! installConfigs; then - echo -e " {CROSS} Failure in dependent config copy function." + printf " %b Failure in dependent config copy function.\\n" "${CROSS}" exit 1 fi # If the user wants to install the dashboard, @@ -1803,7 +1893,7 @@ installPihole() { # Install the logrotate file installLogrotate # Check if FTL is installed - FTLdetect || echo -e " ${CROSS} FTL Engine not installed" + FTLdetect || printf " %b FTL Engine not installed\\n" "${CROSS}" # Configure the firewall if [[ "${useUpdateVars}" == false ]]; then configureFirewall @@ -1819,18 +1909,18 @@ installPihole() { # SELinux checkSelinux() { # If the getenforce command exists, - if command -v getenforce &> /dev/null; then + if is_command getenforce ; then # Store the current mode in a variable enforceMode=$(getenforce) - echo -e "\\n ${INFO} SELinux mode detected: ${enforceMode}" + printf "\\n %b SELinux mode detected: %s\\n" "${INFO}" "${enforceMode}" # If it's enforcing, if [[ "${enforceMode}" == "Enforcing" ]]; then # Explain Pi-hole does not support it yet whiptail --defaultno --title "SELinux Enforcing Detected" --yesno "SELinux is being ENFORCED on your system! \\n\\nPi-hole currently does not support SELinux, but you may still continue with the installation.\\n\\nNote: Web Admin will not be fully functional unless you set your policies correctly\\n\\nContinue installing Pi-hole?" ${r} ${c} || \ - { echo -e "\\n ${COL_LIGHT_RED}SELinux Enforcing detected, exiting installer${COL_NC}"; exit 1; } - echo -e " ${INFO} Continuing installation with SELinux Enforcing - ${INFO} Please refer to official SELinux documentation to create a custom policy" + { printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } + printf " %b Continuing installation with SELinux Enforcing\\n" "${INFO}" + printf " %b Please refer to official SELinux documentation to create a custom policy\\n" "${INFO}" fi fi } @@ -1890,18 +1980,18 @@ update_dialogs() { UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\\n\\nWe have detected an existing install.\\n\\nPlease choose from the following options: \\n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) || \ - { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } + { printf " %bCancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}"; exit 1; } # Set the variable based on if the user chooses case ${UpdateCmd} in # repair, or ${opt1a}) - echo -e " ${INFO} ${opt1a} option selected" + printf " %b %s option selected\\n" "${INFO}" "${opt1a}" useUpdateVars=true ;; # reconfigure, ${opt2a}) - echo -e " ${INFO} ${opt2a} option selected" + printf " %b %s option selected\\n" "${INFO}" "${opt2a}" useUpdateVars=false ;; esac @@ -1939,6 +2029,7 @@ get_available_branches() { cd "${directory}" || return 1 # Get reachable remote branches, but store STDERR as STDOUT variable output=$( { git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1 ) + # echo status for calling function to capture echo "$output" return } @@ -1972,16 +2063,16 @@ checkout_pull_branch() { oldbranch="$(git symbolic-ref HEAD)" str="Switching to branch: '${branch}' from '${oldbranch}'" - echo -ne " ${INFO} $str" + printf " %b %s" "${INFO}" "$str" git checkout "${branch}" --quiet || return 1 - echo -e "${OVER} ${TICK} $str" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "$str" git_pull=$(git pull || return 1) if [[ "$git_pull" == *"up-to-date"* ]]; then - echo -e " ${INFO} ${git_pull}" + printf " %b %s\\n" "${INFO}" "${git_pull}" else - echo -e "$git_pull\\n" + printf "%s\\n" "$git_pull" fi return 0 @@ -1990,17 +2081,17 @@ checkout_pull_branch() { clone_or_update_repos() { # If the user wants to reconfigure, if [[ "${reconfigure}" == true ]]; then - echo " ${INFO} Performing reconfiguration, skipping download of local repos" + printf " %b Performing reconfiguration, skipping download of local repos\\n" "${INFO}" # Reset the Core repo resetRepo ${PI_HOLE_LOCAL_REPO} || \ - { echo -e " ${COL_LIGHT_RED}Unable to reset ${PI_HOLE_LOCAL_REPO}, exiting installer${COL_NC}"; \ + { printf " %bUnable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"; \ exit 1; \ } # If the Web interface was installed, if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then # reset it's repo resetRepo ${webInterfaceDir} || \ - { echo -e " ${COL_LIGHT_RED}Unable to reset ${webInterfaceDir}, exiting installer${COL_NC}"; \ + { printf " %bUnable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}"; \ exit 1; \ } fi @@ -2008,14 +2099,14 @@ clone_or_update_repos() { else # so get git files for Core getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || \ - { echo -e " ${COL_LIGHT_RED}Unable to clone ${piholeGitUrl} into ${PI_HOLE_LOCAL_REPO}, unable to continue${COL_NC}"; \ + { printf " %bUnable to clone %s into %s, unable to continue%b\\n" "${COL_LIGHT_RED}" "${piholeGitUrl}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"; \ exit 1; \ } # If the Web interface was installed, if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then # get the Web git files getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || \ - { echo -e " ${COL_LIGHT_RED}Unable to clone ${webInterfaceGitUrl} into ${webInterfaceDir}, exiting installer${COL_NC}"; \ + { printf " %bUnable to clone %s into ${webInterfaceDir}, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceGitUrl}" "${COL_NC}"; \ exit 1; \ } fi @@ -2028,19 +2119,19 @@ FTLinstall() { local binary="${1}" local latesttag local str="Downloading and Installing FTL" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # Find the latest version tag for FTL latesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep "Location" | awk -F '/' '{print $NF}') # Tags should always start with v, check for that. if [[ ! "${latesttag}" == v* ]]; then - echo -e "${OVER} ${CROSS} ${str}" - echo -e " ${COL_LIGHT_RED}Error: Unable to get latest release location from GitHub${COL_NC}" + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + printf " %bError: Unable to get latest release location from GitHub%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" return 1 fi # Move into the temp ftl directory - pushd "$(mktemp -d)" > /dev/null || { echo "Unable to make temporary directory for FTL binary download"; return 1; } + pushd "$(mktemp -d)" > /dev/null || { printf "Unable to make temporary directory for FTL binary download\\n"; return 1; } # Always replace pihole-FTL.service install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.service" "/etc/init.d/pihole-FTL" @@ -2068,46 +2159,49 @@ FTLinstall() { # If we downloaded binary file (as opposed to text), if sha1sum --status --quiet -c "${binary}".sha1; then - echo -n "transferred... " + printf "transferred... " # Stop FTL stop_service pihole-FTL &> /dev/null # Install the new version with the correct permissions install -T -m 0755 "${binary}" /usr/bin/pihole-FTL # Move back into the original directory the user was in - popd > /dev/null || { echo "Unable to return to original directory after FTL binary download."; return 1; } + popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; } # Install the FTL service - echo -e "${OVER} ${TICK} ${str}" + printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" # dnsmasq can now be stopped and disabled if it exists if which dnsmasq &> /dev/null; then if check_service_active "dnsmasq";then - echo " ${INFO} FTL can now resolve DNS Queries without dnsmasq running separately" + printf " %b FTL can now resolve DNS Queries without dnsmasq running separately\\n" "${INFO}" stop_service dnsmasq disable_service dnsmasq fi fi - #ensure /etc/dnsmasq.conf contains `conf-dir=/etc/dnsmasq.d` - confdir="conf-dir=/etc/dnsmasq.d" - conffile="/etc/dnsmasq.conf" - if ! grep -q "$confdir" "$conffile"; then - echo "$confdir" >> "$conffile" + # Backup existing /etc/dnsmasq.conf if present and ensure that + # /etc/dnsmasq.conf contains only "conf-dir=/etc/dnsmasq.d" + local conffile="/etc/dnsmasq.conf" + if [[ -f "${conffile}" ]]; then + printf " %b Backing up %s to %s.old\\n" "${INFO}" "${conffile}" "${conffile}" + mv "${conffile}" "${conffile}.old" fi + # Create /etc/dnsmasq.conf + echo "conf-dir=/etc/dnsmasq.d" > "${conffile}" return 0 # Otherwise, else # the download failed, so just go back to the original directory - popd > /dev/null || { echo "Unable to return to original directory after FTL binary download."; return 1; } - echo -e "${OVER} ${CROSS} ${str}" - echo -e " ${COL_LIGHT_RED}Error: Download of binary from Github failed${COL_NC}" + popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; } + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" + printf " %bError: Download of %s/%s failed (checksum error)%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}" return 1 fi # Otherwise, else - popd > /dev/null || { echo "Unable to return to original directory after FTL binary download."; return 1; } - echo -e "${OVER} ${CROSS} ${str}" + popd > /dev/null || { printf "Unable to return to original directory after FTL binary download.\\n"; return 1; } + printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" # The URL could not be found - echo -e " ${COL_LIGHT_RED}Error: URL not found${COL_NC}" + printf " %bError: URL %s/%s not found%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}" return 1 fi } @@ -2118,7 +2212,7 @@ get_binary_name() { machine=$(uname -m) local str="Detecting architecture" - echo -ne " ${INFO} ${str}..." + printf " %b %s..." "${INFO}" "${str}" # If the machine is arm or aarch if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then # ARM @@ -2130,32 +2224,27 @@ get_binary_name() { lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') # if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then - echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture" + printf "%b %b Detected ARM-aarch64 architecture\\n" "${OVER}" "${TICK}" # set the binary to be used binary="pihole-FTL-aarch64-linux-gnu" # elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then # if [[ "${rev}" -gt 6 ]]; then - echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv7+)" + printf "%b %b Detected ARM-hf architecture (armv7+)\\n" "${OVER}" "${TICK}" # set the binary to be used binary="pihole-FTL-arm-linux-gnueabihf" # Otherwise, else - echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv6 or lower) Using ARM binary" + printf "%b %b Detected ARM-hf architecture (armv6 or lower) Using ARM binary\\n" "${OVER}" "${TICK}" # set the binary to be used binary="pihole-FTL-arm-linux-gnueabi" fi else - echo -e "${OVER} ${TICK} Detected ARM architecture" + printf "%b %b Detected ARM architecture\\n" "${OVER}" "${TICK}" # set the binary to be used binary="pihole-FTL-arm-linux-gnueabi" fi - elif [[ "${machine}" == "ppc" ]]; then - # PowerPC - echo -e "${OVER} ${TICK} Detected PowerPC architecture" - # set the binary to be used - binary="pihole-FTL-powerpc-linux-gnu" elif [[ "${machine}" == "x86_64" ]]; then # This gives the architecture of packages dpkg installs (for example, "i386") local dpkgarch @@ -2164,22 +2253,22 @@ get_binary_name() { # Special case: This is a 32 bit OS, installed on a 64 bit machine # -> change machine architecture to download the 32 bit executable if [[ "${dpkgarch}" == "i386" ]]; then - echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture" + printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}" binary="pihole-FTL-linux-x86_32" else # 64bit - echo -e "${OVER} ${TICK} Detected x86_64 architecture" + printf "%b %b Detected x86_64 architecture\\n" "${OVER}" "${TICK}" # set the binary to be used binary="pihole-FTL-linux-x86_64" fi else # Something else - we try to use 32bit executable and warn the user if [[ ! "${machine}" == "i686" ]]; then - echo -e "${OVER} ${CROSS} ${str}..." - echo -e " ${INFO} ${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable${COL_NC}" - echo -e " ${INFO} Contact Pi-hole Support if you experience issues (e.g: FTL not running)" + printf "%b %b %s...\\n" "${OVER}" "${CROSS}" "${str}" + printf " %b %bNot able to detect architecture (unknown: %s), trying 32bit executable%b\\n" "${INFO}" "${COL_LIGHT_RED}" "${machine}" "${COL_NC}" + printf " %b Contact Pi-hole Support if you experience issues (e.g: FTL not running)\\n" "${INFO}" else - echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture" + printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}" fi binary="pihole-FTL-linux-x86_32" fi @@ -2190,7 +2279,7 @@ FTLcheckUpdate() { #In the next section we check to see if FTL is already installed (in case of pihole -r). #If the installed version matches the latest version, then check the installed sha1sum of the binary vs the remote sha1sum. If they do not match, then download - echo -e " ${INFO} Checking for existing FTL binary..." + printf " %b Checking for existing FTL binary...\\n" "${INFO}" local ftlLoc ftlLoc=$(which pihole-FTL 2>/dev/null) @@ -2219,7 +2308,8 @@ FTLcheckUpdate() { path="${ftlBranch}/${binary}" # shellcheck disable=SC1090 if ! check_download_exists "$path"; then - echo -e " ${INFO} Branch \"${ftlBranch}\" is not available.\\n ${INFO} Use ${COL_LIGHT_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch." + printf " %b Branch \"%s\" is not available.\\n" "${INFO}" "${ftlBranch}" + printf " %b Use %bpihole checkout ftl [branchname]%b to switch to a valid branch.\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${COL_NC}" return 2 fi @@ -2230,10 +2320,10 @@ FTLcheckUpdate() { localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1) if [[ "${remoteSha1}" != "${localSha1}" ]]; then - echo -e " ${INFO} Checksums do not match, downloading from ftl.pi-hole.net." + printf " %b Checksums do not match, downloading from ftl.pi-hole.net.\\n" "${INFO}" return 0 else - echo -e " ${INFO} Checksum of installed binary matches remote. No need to download!" + printf " %b Checksum of installed binary matches remote. No need to download!\\n" "${INFO}" return 1 fi else @@ -2249,16 +2339,16 @@ FTLcheckUpdate() { if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then return 0 else - echo -e " ${INFO} Latest FTL Binary already installed (${FTLlatesttag}). Confirming Checksum..." + printf " %b Latest FTL Binary already installed (%s). Confirming Checksum...\\n" "${INFO}" "${FTLlatesttag}" remoteSha1=$(curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${FTLversion%$'\r'}/${binary}.sha1" | cut -d ' ' -f 1) localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1) if [[ "${remoteSha1}" != "${localSha1}" ]]; then - echo -e " ${INFO} Corruption detected..." + printf " %b Corruption detected...\\n" "${INFO}" return 0 else - echo -e " ${INFO} Checksum correct. No need to download!" + printf " %b Checksum correct. No need to download!\\n" "${INFO}" return 1 fi fi @@ -2270,14 +2360,11 @@ FTLcheckUpdate() { # Detect suitable FTL binary platform FTLdetect() { - echo "" - echo -e " ${INFO} FTL Checks..." + printf "\\n %b FTL Checks...\\n\\n" "${INFO}" if FTLcheckUpdate ; then FTLinstall "${binary}" || return 1 fi - - echo "" } make_temporary_log() { @@ -2302,37 +2389,37 @@ main() { ######## FIRST CHECK ######## # Must be root to install local str="Root user check" - echo "" + printf "\\n" # If the user's id is zero, if [[ "${EUID}" -eq 0 ]]; then # they are root and all is good - echo -e " ${TICK} ${str}" + printf " %b %s\\n" "${TICK}" "${str}" # Show the Pi-hole logo so people know it's genuine since the logo and name are trademarked show_ascii_berry make_temporary_log # Otherwise, else # They do not have enough privileges, so let the user know - echo -e " ${CROSS} ${str}" - echo -e " ${INFO} ${COL_LIGHT_RED}Script called with non-root privileges${COL_NC}" - echo -e " ${INFO} The Pi-hole requires elevated privileges to install and run" - echo -e " ${INFO} Please check the installer for any concerns regarding this requirement" - echo -e " ${INFO} Make sure to download this script from a trusted source\\n" - echo -ne " ${INFO} Sudo utility check" + printf " %b %s\\n" "${CROSS}" "${str}" + printf " %b %bScript called with non-root privileges%b\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}" + printf " The Pi-hole requires elevated privileges to install and run\\n" + printf " Please check the installer for any concerns regarding this requirement\\n" + printf " Make sure to download this script from a trusted source\\n\\n" + printf " %b Sudo utility check" "${INFO}" # If the sudo command exists, - if command -v sudo &> /dev/null; then - echo -e "${OVER} ${TICK} Sudo utility check" + if is_command sudo ; then + printf "%b %b Sudo utility check\\n" "${OVER}" "${TICK}" # Download the install script and run it with admin rights exec curl -sSL https://raw.githubusercontent.com/pi-hole/pi-hole/master/automated%20install/basic-install.sh | sudo bash "$@" exit $? # Otherwise, else # Let them know they need to run it as root - echo -e "${OVER} ${CROSS} Sudo utility check" - echo -e " ${INFO} Sudo is needed for the Web Interface to run pihole commands\\n" - echo -e " ${INFO} ${COL_LIGHT_RED}Please re-run this installer as root${COL_NC}" + printf "%b %b Sudo utility check\\n" "${OVER}" "${CROSS}" + printf " %b Sudo is needed for the Web Interface to run pihole commands\\n\\n" "${INFO}" + printf " %b %bPlease re-run this installer as root${COL_NC}\\n" "${INFO}" "${COL_LIGHT_RED}" exit 1 fi fi @@ -2344,9 +2431,11 @@ main() { if [[ -f "${setupVars}" ]]; then # if it's running unattended, if [[ "${runUnattended}" == true ]]; then - echo -e " ${INFO} Performing unattended setup, no whiptail dialogs will be displayed" + printf " %b Performing unattended setup, no whiptail dialogs will be displayed\\n" "${INFO}" # Use the setup variables useUpdateVars=true + # also disable debconf-apt-progress dialogs + export DEBIAN_FRONTEND="noninteractive" # Otherwise, else # show the available options (repair/reconfigure) @@ -2357,7 +2446,7 @@ main() { # Start the installer # Verify there is enough disk space for the install if [[ "${skipSpaceCheck}" == true ]]; then - echo -e " ${INFO} Skipping free disk space verification" + printf" %b Skipping free disk space verification\\n" "${INFO}" else verifyFreeDiskSpace fi @@ -2393,9 +2482,22 @@ main() { setAdminFlag # Let the user decide if they want query logging enabled... setLogging + # Let the user decide the FTL privacy level + setPrivacyLevel else + # Setup adlist file if not exists + installDefaultBlocklists + # Source ${setupVars} to use predefined user variables in the functions source ${setupVars} + + # Get the privacy level if it exists (default is 0) + if [[ -f "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then + PRIVACY_LEVEL=$(sed -ne 's/PRIVACYLEVEL=\(.*\)/\1/p' "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf") + + # If no setting was found, default to 0 + PRIVACY_LEVEL="${PRIVACY_LEVEL:-0}" + fi fi # Clone/Update the repos clone_or_update_repos @@ -2454,16 +2556,19 @@ main() { start_service lighttpd enable_service lighttpd else - echo -e " ${INFO} Lighttpd is disabled, skipping service restart" + printf " %b Lighttpd is disabled, skipping service restart\\n" "${INFO}" fi fi - echo -e " ${INFO} Restarting services..." + printf " %b Restarting services...\\n" "${INFO}" # Start services # Enable FTL - start_service pihole-FTL + # Ensure the service is enabled before trying to start it + # Fixes a problem reported on Ubuntu 18.04 where trying to start + # the service before enabling causes installer to exit enable_service pihole-FTL + start_service pihole-FTL # Download and compile the aggregated block list runGravity @@ -2481,33 +2586,32 @@ main() { # If there is a password, if (( ${#pw} > 0 )) ; then # display the password - echo -e " ${INFO} Web Interface password: ${COL_LIGHT_GREEN}${pw}${COL_NC}" - echo -e " ${INFO} This can be changed using 'pihole -a -p'\\n" + printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${pw}" "${COL_NC}" + printf " %b This can be changed using 'pihole -a -p'\\n\\n" "${INFO}" fi fi if [[ "${useUpdateVars}" == false ]]; then # If the Web interface was installed, if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then - echo -e " ${INFO} View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" - echo "" + printf " %b View the web interface at http://pi.hole/admin or http://%s/admin\\n\\n" "${INFO}" "${IPV4_ADDRESS%/*}" fi # Explain to the user how to use Pi-hole as their DNS server - echo -e " ${INFO} You may now configure your devices to use the Pi-hole as their DNS server" - [[ -n "${IPV4_ADDRESS%/*}" ]] && echo -e " ${INFO} Pi-hole DNS (IPv4): ${IPV4_ADDRESS%/*}" - [[ -n "${IPV6_ADDRESS}" ]] && echo -e " ${INFO} Pi-hole DNS (IPv6): ${IPV6_ADDRESS}" - echo -e " ${INFO} If you set a new IP address, please restart the server running the Pi-hole" + printf " %b You may now configure your devices to use the Pi-hole as their DNS server\\n" "${INFO}" + [[ -n "${IPV4_ADDRESS%/*}" ]] && printf " %b Pi-hole DNS (IPv4): %s\\n" "${INFO}" "${IPV4_ADDRESS%/*}" + [[ -n "${IPV6_ADDRESS}" ]] && printf " %b Pi-hole DNS (IPv6): %s\\n" "${INFO}" "${IPV6_ADDRESS}" + printf " %b If you set a new IP address, please restart the server running the Pi-hole\\n" "${INFO}" INSTALL_TYPE="Installation" else INSTALL_TYPE="Update" fi # Display where the log file is - echo -e "\\n ${INFO} The install log is located at: ${installLogLoc} - ${COL_LIGHT_GREEN}${INSTALL_TYPE} Complete! ${COL_NC}" + printf "\\n %b The install log is located at: %s\\n" "${INFO}" "${installLogLoc}" + printf "%b%s Complete! %b\\n" "${COL_LIGHT_GREEN}" "${INSTALL_TYPE}" "${COL_NC}" if [[ "${INSTALL_TYPE}" == "Update" ]]; then - echo "" + printf "\\n" /usr/local/bin/pihole version --current fi } diff --git a/automated install/uninstall.sh b/automated install/uninstall.sh index 9322de92..52760cfb 100755 --- a/automated install/uninstall.sh +++ b/automated install/uninstall.sh @@ -106,7 +106,7 @@ removeNoPurge() { ${SUDO} rm -rf /var/www/html/pihole &> /dev/null ${SUDO} rm -f /var/www/html/index.lighttpd.orig &> /dev/null - # If the web directory is empty after removing these files, then the parent html folder can be removed. + # If the web directory is empty after removing these files, then the parent html directory can be removed. if [ -d "/var/www/html" ]; then if [[ ! "$(ls -A /var/www/html)" ]]; then ${SUDO} rm -rf /var/www/html &> /dev/null @@ -68,11 +68,35 @@ else exit 1 fi +# Source pihole-FTL from install script +pihole_FTL="${piholeDir}/pihole-FTL.conf" +if [[ -f "${pihole_FTL}" ]]; then + source "${pihole_FTL}" +fi + +if [[ -z "${BLOCKINGMODE}" ]] ; then + BLOCKINGMODE="NULL" +fi + # Determine if superseded pihole.conf exists if [[ -r "${piholeDir}/pihole.conf" ]]; then echo -e " ${COL_LIGHT_RED}Ignoring overrides specified within pihole.conf! ${COL_NC}" fi +# Determine if Pi-hole blocking is disabled +# If this is the case, we want to update +# gravity.list.bck and black.list.bck instead of +# gravity.list and black.list +detect_pihole_blocking_status() { + if [[ "${BLOCKING_ENABLED}" == false ]]; then + echo -e " ${INFO} Pi-hole blocking is disabled" + adList="${adList}.bck" + blackList="${blackList}.bck" + else + echo -e " ${INFO} Pi-hole blocking is enabled" + fi +} + # Determine if DNS resolution is available before proceeding gravity_CheckDNSResolutionAvailable() { local lookupDomain="pi.hole" @@ -182,7 +206,7 @@ gravity_SetDownloadOptions() { activeDomains[$i]="${saveLocation}" # Default user-agent (for Cloudflare's Browser Integrity Check: https://support.cloudflare.com/hc/en-us/articles/200170086-What-does-the-Browser-Integrity-Check-do-) - agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2227.0 Safari/537.36" + agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36" # Provide special commands for blocklists which may need them case "${domain}" in @@ -216,6 +240,39 @@ gravity_DownloadBlocklistFromUrl() { str="Status:" echo -ne " ${INFO} ${str} Pending..." + blocked=false + case $BLOCKINGMODE in + "IP-NODATA-AAAA"|"IP") + if [[ $(dig "${domain}" +short | grep "${IPV4_ADDRESS}" -c) -ge 1 ]]; then + blocked=true + fi;; + "NXDOMAIN") + if [[ $(dig "${domain}" | grep "NXDOMAIN" -c) -ge 1 ]]; then + blocked=true + fi;; + "NULL"|*) + if [[ $(dig "${domain}" +short | grep "0.0.0.0" -c) -ge 1 ]]; then + blocked=true + fi;; + esac + + if [[ "${blocked}" == true ]]; then + printf -v ip_addr "%s" "${PIHOLE_DNS_1%#*}" + if [[ ${PIHOLE_DNS_1} != *"#"* ]]; then + port=53 + else + printf -v port "%s" "${PIHOLE_DNS_1#*#}" + fi + ip=$(dig "@${ip_addr}" -p "${port}" +short "${domain}") + if [[ $(echo "${url}" | awk -F '://' '{print $1}') = "https" ]]; then + port=443; + else port=80 + fi + bad_list=$(pihole -q -adlist hosts-file.net | head -n1 | awk -F 'Match found in ' '{print $2}') + echo -e "${OVER} ${CROSS} ${str} ${domain} is blocked by ${bad_list%:}. Using DNS on ${PIHOLE_DNS_1} to download ${url}"; + echo -ne " ${INFO} ${str} Pending..." + cmd_ext="--resolve $domain:$port:$ip $cmd_ext" + fi # shellcheck disable=SC2086 httpCode=$(curl -s -L ${cmd_ext} ${heisenbergCompensator} -w "%{http_code}" -A "${agent}" "${url}" -o "${patternBuffer}" 2> /dev/null) @@ -464,7 +521,7 @@ gravity_ShowBlockCount() { fi if [[ -f "${regexFile}" ]]; then - num=$(grep -c "^(?!#)" "${regexFile}") + num=$(grep -cv "^#" "${regexFile}") echo -e " ${INFO} Number of regex filters: ${num}" fi } @@ -522,7 +579,7 @@ gravity_ParseBlacklistDomains() { mv "${piholeDir}/${whitelistMatter}" "${piholeDir}/${accretionDisc}" else # There was no whitelist file, so use preEventHorizon instead of whitelistMatter. - mv "${piholeDir}/${preEventHorizon}" "${piholeDir}/${accretionDisc}" + cp "${piholeDir}/${preEventHorizon}" "${piholeDir}/${accretionDisc}" fi # Move the file over as /etc/pihole/gravity.list so dnsmasq can use it @@ -621,6 +678,8 @@ if [[ "${forceDelete:-}" == true ]]; then echo -e "${OVER} ${TICK} ${str}" fi +detect_pihole_blocking_status + # Determine which functions to run if [[ "${skipDownload}" == false ]]; then # Gravity needs to download blocklists @@ -10,7 +10,14 @@ # Please see LICENSE file for your rights under this license. readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" -readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" +readonly gravitylist="/etc/pihole/gravity.list" +readonly blacklist="/etc/pihole/black.list" + +# setupVars is not readonly here because in some funcitons (checkout), +# it might get set again when the installer is sourced. This causes an +# error due to modifying a readonly variable. +setupVars="/etc/pihole/setupVars.conf" + readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE" source "${colfile}" @@ -73,7 +80,7 @@ reconfigurePiholeFunc() { updateGravityFunc() { "${PI_HOLE_SCRIPT_DIR}"/gravity.sh "$@" - exit 0 + exit $? } queryFunc() { @@ -127,9 +134,11 @@ restartDNS() { if [[ "${status}" -eq 0 ]]; then [[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}" + return 0 else [[ ! -t 1 ]] && local OVER="" echo -e "${OVER} ${CROSS} ${output}" + return 1 fi } @@ -146,10 +155,13 @@ Time: elif [[ "${1}" == "0" ]]; then # Disable Pi-hole - sed -i 's/^addn-hosts=\/etc\/pihole\/gravity.list/#addn-hosts=\/etc\/pihole\/gravity.list/' /etc/dnsmasq.d/01-pihole.conf - sed -i 's/^addn-hosts=\/etc\/pihole\/black.list/#addn-hosts=\/etc\/pihole\/black.list/' /etc/dnsmasq.d/01-pihole.conf - if [[ -e "$wildcardlist" ]]; then - mv "$wildcardlist" "/etc/pihole/wildcard.list" + if [[ -e "${gravitylist}" ]]; then + mv "${gravitylist}" "${gravitylist}.bck" + echo "" > "${gravitylist}" + fi + if [[ -e "${blacklist}" ]]; then + mv "${blacklist}" "${blacklist}.bck" + echo "" > "${blacklist}" fi if [[ $# > 1 ]]; then local error=false @@ -187,19 +199,25 @@ Time: fi local str="Pi-hole Disabled" + sed -i "/BLOCKING_ENABLED=/d" "${setupVars}" + echo "BLOCKING_ENABLED=false" >> "${setupVars}" fi else # Enable Pi-hole echo -e " ${INFO} Enabling blocking" local str="Pi-hole Enabled" - sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf - if [[ -e "/etc/pihole/wildcard.list" ]]; then - mv "/etc/pihole/wildcard.list" "$wildcardlist" + if [[ -e "${gravitylist}.bck" ]]; then + mv "${gravitylist}.bck" "${gravitylist}" fi + if [[ -e "${blacklist}.bck" ]]; then + mv "${blacklist}.bck" "${blacklist}" + fi + sed -i "/BLOCKING_ENABLED=/d" "${setupVars}" + echo "BLOCKING_ENABLED=true" >> "${setupVars}" fi - restartDNS + restartDNS reload echo -e "${OVER} ${TICK} ${str}" } @@ -242,8 +260,6 @@ Options: } statusFunc() { - local addnConfigs - # Determine if service is running on port 53 (Cr: https://superuser.com/a/806331) if (echo > /dev/tcp/127.0.0.1/53) >/dev/null 2>&1; then if [[ "${1}" != "web" ]]; then @@ -257,16 +273,14 @@ statusFunc() { return 0 fi - # Determine if Pi-hole's addn-hosts configs are commented out - addnConfigs=$(grep -i "addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf) - - if [[ "${addnConfigs}" =~ "#" ]]; then + # Determine if Pi-hole's blocking is enabled + if grep -q "BLOCKING_ENABLED=false" /etc/pihole/setupVars.conf; then # A config is commented out case "${1}" in "web") echo 0;; *) echo -e " ${CROSS} Pi-hole blocking is Disabled";; esac - elif [[ -n "${addnConfigs}" ]]; then + elif grep -q "BLOCKING_ENABLED=true" /etc/pihole/setupVars.conf; then # Configs are set case "${1}" in "web") echo 1;; @@ -276,11 +290,10 @@ statusFunc() { # No configs were found case "${1}" in "web") echo 99;; - *) echo -e " ${INFO} No hosts file linked to dnsmasq, adding it in enabled state";; + *) echo -e " ${INFO} Pi-hole blocking will be enabled";; esac - # Add addn-host= to dnsmasq - echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf - restartDNS + # Enable blocking + pihole enable fi } @@ -303,7 +316,7 @@ tailFunc() { # Colour everything else as gray tail -f /var/log/pihole.log | sed -E \ -e "s,($(date +'%b %d ')| dnsmasq[.*[0-9]]),,g" \ - -e "s,(.*(gravity.list|black.list| config ).* is (${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \ + -e "s,(.*(gravity.list|black.list|regex.list| config ).* is (0.0.0.0|::|NXDOMAIN|${IPV4_ADDRESS%/*}|${IPV6_ADDRESS:-NULL}).*),${COL_RED}&${COL_NC}," \ -e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \ -e "s,.*,${COL_GRAY}&${COL_NC}," exit 0 diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 876b06eb..a2593d83 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -81,6 +81,7 @@ def test_setupVars_saved_to_file(Pihole): {} mkdir -p /etc/dnsmasq.d version_check_dnsmasq + echo "" > /etc/pihole/pihole-FTL.conf finalExports cat /etc/pihole/setupVars.conf '''.format(set_setup_vars)) @@ -501,8 +502,10 @@ def test_FTL_download_unknown_fails_no_errors(Pihole): ''') expected_stdout = cross_box + ' Downloading and Installing FTL' assert expected_stdout in download_binary.stdout - error = 'Error: URL not found' - assert error in download_binary.stdout + error1 = 'Error: URL https://github.com/pi-hole/FTL/releases/download/' + assert error1 in download_binary.stdout + error2 = 'not found' + assert error2 in download_binary.stdout def test_FTL_binary_installed_and_responsive_no_errors(Pihole): diff --git a/test/test_centos_fedora_support.py b/test/test_centos_fedora_support.py index 8318e44a..df53d73f 100644 --- a/test/test_centos_fedora_support.py +++ b/test/test_centos_fedora_support.py @@ -31,20 +31,13 @@ def test_release_supported_version_check_centos(Pihole): ''' confirms installer exits on unsupported releases of CentOS ''' - # mock CentOS release < 7 (unsupported) - mock_command_2( - 'rpm', - {"-q --queryformat '%{VERSION}' centos-release'": ( - '5', - '0' - )}, - Pihole - ) + # modify /etc/redhat-release to mock an unsupported CentOS release + Pihole.run('echo "CentOS Linux release 6.9" > /etc/redhat-release') distro_check = Pihole.run(''' source /opt/pihole/basic-install.sh distro_check ''') - expected_stdout = cross_box + (' CentOS is not suported.') + expected_stdout = cross_box + (' CentOS 6 is not supported.') assert expected_stdout in distro_check.stdout expected_stdout = 'Please update to CentOS release 7 or later' assert expected_stdout in distro_check.stdout |