Merge pull request #1776 from pi-hole/release/3.2

Pi-hole core v3.2
This commit is contained in:
Jacob Salmela 2017-12-06 21:37:10 -06:00 committed by GitHub
commit 5ba413569e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 5127 additions and 2568 deletions

View file

@ -1,33 +1,37 @@
**In raising this issue, I confirm the following (please check boxes, eg [X]) Failure to fill the template will close your issue:** **In raising this issue, I confirm the following:** `{please fill the checkboxes, e.g: [X]}`
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md). - [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md).
- [] The issue I am reporting can be *replicated* - [] The issue I am reporting can be *replicated*.
- [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)). - [] The issue I am reporting isn't a duplicate (see [FAQs](https://github.com/pi-hole/pi-hole/wiki/FAQs), [closed issues](https://github.com/pi-hole/pi-hole/issues?utf8=%E2%9C%93&q=is%3Aissue%20is%3Aclosed%20), and [open issues](https://github.com/pi-hole/pi-hole/issues)).
**How familiar are you with the codebase?:** **How familiar are you with the the source code relevant to this issue?:**
_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_ `{Replace this with a number from 1 to 10. 1 being not familiar, and 10 being very familiar}`
--- ---
**[BUG REPORT | OTHER]:** **Expected behaviour:**
Please [submit your feature request here](https://discourse.pi-hole.net/c/feature-requests), so it is votable by the community. It's also easier for us to track. `{A detailed description of what you expect to see}`
**[BUG | ISSUE] Expected Behaviour:** **Actual behaviour:**
`{A detailed description and/or screenshots of what you do see}`
**[BUG | ISSUE] Actual Behaviour:** **Steps to reproduce:**
`{Detailed steps of how we can reproduce this}`
**[BUG | ISSUE] Steps to reproduce:** **Debug token provided by [uploading `pihole -d` log](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#debug):**
- `{Alphanumeric token}`
-
-
-
**(Optional) Debug token generated by `pihole -d`:** **Troubleshooting undertaken, and/or other relevant information:**
`<token>` `{Steps of what you have done to fix this}`
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._ > * `{Please delete this quoted section when opening your issue}`
> * You must follow the template instructions. Failure to do so will result in your issue being closed.
> * Please [submit any feature requests here](https://discourse.pi-hole.net/c/feature-requests), so it is votable and trackable by the community.
> * Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time.
> * Detail helps us understand and resolve an issue quicker, but please ensure it's relevant.
> * _This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._

View file

@ -1,19 +1,32 @@
**By submitting this pull request, I confirm the following (please check boxes, eg [X]) _Failure to fill the template will close your PR_:** **By submitting this pull request, I confirm the following:** `{please fill any appropriate checkboxes, e.g: [X]}`
***Please submit all pull requests against the `development` branch. Failure to do so will delay or deny your request*** `{Please ensure that your pull request is for the 'development' branch!}`
- [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md). - [] I have read and understood the [contributors guide](https://github.com/pi-hole/pi-hole/blob/master/CONTRIBUTING.md), as well as this entire template.
- [] I have checked that [another pull request](https://github.com/pi-hole/pi-hole/pulls) for this purpose does not exist. - [] I have made only one major change in my proposed changes.
- [] I have considered, and confirmed that this submission will be valuable to others. - [] I have commented my proposed changes within the code.
- [] I accept that this submission may not be used, and the pull request closed at the will of the maintainer. - [] I have tested my proposed changes, and have included unit tests where possible.
- [] I give this submission freely, and claim no ownership to its content. - [] I am willing to help maintain this change if there are issues with it later.
- [] I give this submission freely and claim no ownership.
**How familiar are you with the codebase?:** - [] It is compatible with the [EUPL 1.2 license](https://opensource.org/licenses/EUPL-1.1)
- [] I have squashed any insignificant commits. ([`git rebase`](http://gitready.com/advanced/2009/02/10/squashing-commits-with-rebase.html))
_{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_ - [] I have Signed Off all commits. ([`git commit --signoff`](https://git-scm.com/docs/git-commit#git-commit---signoff))
--- ---
_{replace this line with your pull request content}_
**What does this PR aim to accomplish?:**
_This template was created based on the work of [`udemy-dl`](https://github.com/nishad/udemy-dl/blob/master/LICENSE)._ `{A detailed description, screenshots (if necessary), as well as links to any relevant GitHub issues}`
**How does this PR accomplish the above?:**
`{A detailed description (such as a changelog) and screenshots (if necessary) of the implemented fix}`
**What documentation changes (if any) are needed to support this PR?:**
`{A detailed list of any necessary changes}`
> * `{Please delete this quoted section when opening your pull request}`
> * You must follow the template instructions. Failure to do so will result in your issue being closed.
> * Please respect that Pi-hole is developed by volunteers, who can only reply in their spare time.
> * Detail helps us understand an issue quicker, but please ensure it's relevant.

1
.gitignore vendored
View file

@ -3,4 +3,3 @@
*.swp *.swp
__pycache__ __pycache__
.cache .cache
.pullapprove.yml

285
README.md
View file

@ -1,176 +1,217 @@
<p align="center"> <p align="center">
<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"></a> <a href="https://pi-hole.net"><img src="https://pi-hole.github.io/graphics/Vortex/Vortex_with_text.png" width="150" height="255" alt="Pi-hole"></a><br/>
<a href="https://www.codacy.com/app/Pi-hole/pi-hole?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=pi-hole/pi-hole&amp;utm_campaign=Badge_Grade"><img src="https://api.codacy.com/project/badge/Grade/c558a0f8d7124c99b02b84f0f5564238"/></a> <b>Network-wide ad blocking via your own Linux hardware</b><br/>
<a href=https://travis-ci.org/pi-hole/pi-hole><img src="https://travis-ci.org/pi-hole/pi-hole.svg?branch=development"></a>
</p> </p>
<p align="center"> The Pi-hole is a [DNS sinkhole](https://en.wikipedia.org/wiki/DNS_Sinkhole) that protects your devices from unwanted content, without installing any client-side software.
<a href=https://discourse.pi-hole.net><img src="https://assets.pi-hole.net/static/Vortex_with_text_and_TM.png" width=210></a>
</p>
## The multi-platform, network-wide ad blocker - **Easy-to-install**: our versatile installer walks you through the process, and [takes less than ten minutes](https://www.youtube.com/watch?v=vKWjx1AQYgs)
- **Resolute**: content is blocked in _non-browser locations_, such as ad-laden mobile apps and smart TVs
- **Responsive**: seamlessly speeds up the feel of everyday browsing by caching DNS queries
- **Lightweight**: runs smoothly with [minimal hardware and software requirements](https://discourse.pi-hole.net/t/hardware-software-requirements/273)
- **Robust**: a command line interface that is quality assured for interoperability
- **Insightful**: a beautiful responsive Web Interface dashboard to view and control your Pi-hole
- **Versatile**: can optionally function as a [DHCP server](https://discourse.pi-hole.net/t/how-do-i-use-pi-holes-built-in-dhcp-server-and-why-would-i-want-to/3026), ensuring *all* your devices are protected automatically
- **Scalable**: [capable of handling hundreds of millions of queries](https://pi-hole.net/2017/05/24/how-much-traffic-can-pi-hole-handle/) when installed on server-grade hardware
- **Modern**: blocks ads over both IPv4 and IPv6
- **Free**: open source software which helps ensure _you_ are the sole person in control of your privacy
Block ads for **all** your devices _without_ the need to install client-side software. The Pi-hole™ blocks ads at the DNS-level, so all your devices are protected. -----
<a href="https://www.codacy.com/app/Pi-hole/pi-hole?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=pi-hole/pi-hole&amp;utm_campaign=Badge_Grade"><img src="https://api.codacy.com/project/badge/Grade/c558a0f8d7124c99b02b84f0f5564238" alt="Codacy Grade"/></a>
<a href="https://travis-ci.org/pi-hole/pi-hole"><img src="https://travis-ci.org/pi-hole/pi-hole.svg?branch=development" alt="Travis Build Status"/></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>
- Web Browsers ## One-Step Automated Install
- Cell Phones Those who want to get started quickly and conveniently, may install Pi-hole using the following command:
- Smart TV's
- Internet-connected home automation
- Anything that communicates with the Internet
<p align="center"> #### `curl -sSL https://install.pi-hole.net | bash`
<a href=http://www.digitalocean.com/?refcode=344d234950e1><img src="https://assets.pi-hole.net/static/DOHostingSlug.png"></a>
</p>
## Your Support Still Matters ## Alternative Install Methods
[Piping to `bash` is controversial](https://pi-hole.net/2016/07/25/curling-and-piping-to-bash), as it prevents you from [reading code that is about to run](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) on your system. Therefore, we provide these alternative installation methods which allow code review before installation:
Digital Ocean helps with our infrastructure, but our developers are all volunteers so *your donations help keep us innovating*. Sending a donation using our links below helps us offset a portion of our monthly costs.
- ![Paypal](https://assets.pi-hole.net/static/paypal.png) [Donate via PayPal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY)
- ![Bitcoin](https://assets.pi-hole.net/static/Bitcoin.png) Bitcoin Address: 1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL
### One-Step Automated Install
1. Install a [supported operating system](https://discourse.pi-hole.net/t/hardware-software-requirements/273/1)
2. Run the command below (it downloads [this script](https://github.com/pi-hole/pi-hole/blob/master/automated%20install/basic-install.sh) in case you want to read over it first!)
### `curl -sSL https://install.pi-hole.net | bash`
#### Alternative Semi-Automated Install Methods
_If you wish to read over the script before running it, run `nano basic-install.sh` to open the file in a text viewer._
##### Clone our repository and run the automated installer from your device.
### Method 1: Clone our repository and run
``` ```
git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole git clone --depth 1 https://github.com/pi-hole/pi-hole.git Pi-hole
cd Pi-hole/automated\ install/ cd "Pi-hole/automated install/"
bash basic-install.sh sudo bash basic-install.sh
``` ```
##### Or ### Method 2: Manually download the installer and run
```
```bash
wget -O basic-install.sh https://install.pi-hole.net wget -O basic-install.sh https://install.pi-hole.net
bash basic-install.sh sudo bash basic-install.sh
``` ```
Once installed, [configure your router to have **DHCP clients use the Pi 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) and then any device that connects to your network will have ads blocked without any further configuration. Alternatively, you can manually set each device to use Pi-hole™ as their DNS server. ## Post-install: Make your network take advantage of Pi-hole
## What is Pi-hole™ and how do I install it? 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.
<p align="center">
<a href=https://www.youtube.com/watch?v=vKWjx1AQYgs><img src="https://assets.pi-hole.net/static/video-explainer.png"></a>
</p>
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).
## Get Help Or Connect With Us On The Web As a last resort, you can always manually set each device to use Pi-hole as their DNS server.
- [Users Forum](https://discourse.pi-hole.net/) -----
- [FAQs](https://discourse.pi-hole.net/c/faqs)
- [Wiki](https://github.com/pi-hole/pi-hole/wiki)
- ![Twitter](https://assets.pi-hole.net/static/twitter.png) [Tweet @The_Pi_Hole](https://twitter.com/The_Pi_Hole)
- ![Reddit](https://assets.pi-hole.net/static/reddit.png) [Reddit /r/pihole](https://www.reddit.com/r/pihole/)
- ![YouTube](https://assets.pi-hole.net/static/youtube.png) [Pi-hole channel](https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w)
- [![Join the chat at https://gitter.im/pi-hole/pi-hole](https://badges.gitter.im/pi-hole/pi-hole.svg)](https://gitter.im/pi-hole/pi-hole?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
## Technical Details ## 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.
The Pi-hole™ is an **advertising-aware DNS/Web server**. If an ad domain is queried, a small Web page or GIF is delivered in place of the advertisement. Make no mistake: **your support is absolutely vital to help keep us innovating!**
### Gravity ### Donations
Sending a donation using our links below is **extremely helpful** in offsetting a portion of our monthly expenses:
The [gravity.sh](https://github.com/pi-hole/pi-hole/blob/master/gravity.sh) does most of the magic. The script pulls in ad domains from many sources and compiles them into a single list of [over 1.6 million entries](http://jacobsalmela.com/block-millions-ads-network-wide-with-a-raspberry-pi-hole-2-0) (if you decide to use the [mahakala list](https://github.com/pi-hole/pi-hole/commit/963eacfe0537a7abddf30441c754c67ca1e40965)). This script is controlled by the `pihole` command. Please run `pihole -h` to see what commands can be run via `pihole`. &nbsp;<img src="https://pi-hole.github.io/graphics/Badges/paypal-badge-black.svg" width="24" height="24" alt="PP"/> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3J2L3Z4DHW9UY">Donate via PayPal</a><br/>
&nbsp;<img src="https://pi-hole.github.io/graphics/Badges/bitcoin-badge-black.svg" width="24" height="24" alt="BTC"/> Bitcoin Address: <code>1GKnevUnVaQM2pQieMyeHkpr8DXfkpfAtL</code>
### Alternative support
If you'd rather not donate (_which is okay!_), there are other ways you can help support us:
- [Digital Ocean](http://www.digitalocean.com/?refcode=344d234950e1) affiliate link
- [Vultr](http://www.vultr.com/?ref=7190426) affiliate link
- [UNIXstickers.com](http://unixstickers.refr.cc/jacobs) affiliate link
- [Pi-hole Swag Store](https://pi-hole.net/shop/)
- Spreading the word about our software, and how you have benefited from it
#### Other Operating Systems ### Contributing via GitHub
We welcome _everyone_ to contribute to issue reports, suggest new features, and create pull requests.
The automated install is only for a clean install of a Debian family or Fedora based system, such as the Raspberry Pi. However, this script will work for most UNIX-like systems, some with some slight **modifications** that we can help you work through. If you can install `dnsmasq` and a web server, it should work OK. If there are other platforms you'd like supported, let us know. 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.
### Web Interface 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.
The [Web interface](https://github.com/pi-hole/AdminLTE#pi-hole-admin-dashboard) will be installed automatically so you can view stats and change settings. You can find it at: ### Presentations about Pi-hole
Word-of-mouth continues to help our project grow immensely, and so we are helping make this easier for people.
`http://192.168.1.x/admin/index.php` or `http://pi.hole/admin` If you are going to be presenting Pi-hole at a conference, meetup or even a school project, [get in touch with us](https://pi-hole.net/2017/05/17/giving-a-presentation-on-pi-hole-contact-us-first-for-some-goodies-and-support/) so we can hook you up with free swag to hand out to your audience!
![Pi-hole Advanced Stats Dashboard](https://assets.pi-hole.net/static/dashboard212.png) -----
### Whitelist and blacklist ## Getting in touch with us
While we are primarily reachable on our <a href="https://discourse.pi-hole.net/">Discourse User Forum</a>, we can also be found on a variety of social media outlets. **Please be sure to check the FAQ's** before starting a new discussion, as we do not have the spare time to reply to every request for assistance.
Domains can be whitelisted and blacklisted using either the web interface or the command line. See [the wiki page](https://github.com/pi-hole/pi-hole/wiki/Whitelisting-and-Blacklisting) for more details <ul>
<p align="center"> <li><a href="https://discourse.pi-hole.net/c/faqs">Frequently Asked Questions</a></li>
<a href=https://github.com/pi-hole/pi-hole/wiki/Whitelisting-and-Blacklisting><img src="https://assets.pi-hole.net/static/whitelist212.png"></a> <li><a href="https://github.com/pi-hole/pi-hole/wiki">Pi-hole Wiki</a></li>
</p> <li><a href="https://discourse.pi-hole.net/c/feature-requests?order=votes">Feature Requests</a></li>
</ul>
<br/>
<ul>
<li><a href="https://discourse.pi-hole.net/">Discourse User Forum</a></li>
<li><a href="https://www.reddit.com/r/pihole/">Reddit</a></li>
<li><a href="https://gitter.im/pi-hole/pi-hole">Gitter</a> (Real-time chat)</li>
<li><a href="https://twitter.com/The_Pi_Hole">Twitter</a></li>
<li><a href="https://www.youtube.com/channel/UCT5kq9w0wSjogzJb81C9U0w">YouTube</a></li>
<li><a href="https://www.facebook.com/ThePiHole/">Facebook</a></li>
</ul>
### Settings -----
The settings page lets you control and configure your Pi-hole™. You can do things like: ## 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`.
- enable Pi-hole's built-in DHCP server <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>
- exclude domains from the graphs
- configure upstream DNS servers
- and more!
![Settings page](https://assets.pi-hole.net/static/settings212.png) Some notable features include:
* [Whitelisting, Blacklisting and Wildcards](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#whitelisting-blacklisting-and-wildcards)
* [Debugging utility](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#debugger)
* [Viewing the live log file](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#tail)
* [Real-time Statistics via `ssh`](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#chronometer) or [your TFT LCD screen](http://www.amazon.com/exec/obidos/ASIN/B00ID39LM4/pihole09-20)
* [Updating Ad Lists](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#gravity)
* [Querying Ad Lists for blocked domains](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#query)
* [Enabling and Disabling Pi-hole](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown#enable--disable)
* ... and *many* more!
#### Built-in DHCP Server You can read our [Core Feature Breakdown](https://github.com/pi-hole/pi-hole/wiki/Core-Function-Breakdown), as well as read up on [example usage](https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738) for more information.
Pi-hole™ ships with a built-in DHCP server. This allows you to let your network devices use Pi-hole™ as their DNS server if your router does not let you adjust the DHCP options. ### The Web Interface Dashboard
<p align="center"> This [optional dashboard](https://github.com/pi-hole/AdminLTE) allows you to view stats, change settings, and configure your Pi-hole. It's the power of the Command Line Interface, with none of the learning curve!
<a href=hhttps://discourse.pi-hole.net/t/how-do-i-configure-my-devices-to-use-pi-hole-as-their-dns-server/245><img src="https://assets.pi-hole.net/static/piholedhcpserver.png"></a>
</p>
## API <a href="https://pi-hole.github.io/graphics/Screenshots/dashboard.png"><img src="https://pi-hole.github.io/graphics/Screenshots/dashboard.png" width="888" height="522" alt="Pi-hole Dashboard"/></a>
A basic read-only API can be accessed at `/admin/api.php`. It returns the following JSON: Some notable features include:
* Mobile friendly interface
* Password protection
* 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
* The ability to easily manage and configure Pi-hole features
* ... and all the main features of the Command Line Interface!
``` json There are several ways to [access the dashboard](https://discourse.pi-hole.net/t/how-do-i-access-pi-holes-dashboard-admin-interface/3168):
{
"domains_being_blocked": "136708",
"dns_queries_today": "18108",
"ads_blocked_today": "14648",
"ads_percentage_today": "80.89"
}
```
The same output can be achieved on the CLI by running `chronometer.sh -j` 1. `http://<IP_ADDPRESS_OF_YOUR_PI_HOLE>/admin/`
2. `http:/pi.hole/admin/` (when using Pi-hole as your DNS server)
3. `http://pi.hole/` (when using Pi-hole as your DNS server)
## Real-time Statistics ## The Faster-Than-Light Engine
The [FTL Engine](https://github.com/pi-hole/FTL) is a lightweight, purpose-built daemon used to provide statistics needed for the Web Interface, and its API can be easily integrated into your own projects. As the name implies, FTL does this all *very quickly*!
You can view [real-time stats](https://discourse.pi-hole.net/t/how-do-i-view-my-pi-holes-stats-over-ssh-or-on-an-lcd-using-chronometer/240) via `ssh` or on an [2.8" LCD screen](http://amzn.to/1P0q1Fj). This is accomplished via [`chronometer.sh`](https://github.com/pi-hole/pi-hole/blob/master/advanced/Scripts/chronometer.sh). ![Pi-hole LCD](http://i.imgur.com/nBEqycp.jpg) Some of the statistics you can integrate include:
* Total number of domains being blocked
* Total number of DNS queries today
* Total number of ads blocked today
* Percentage of ads blocked
* Unique domains
* Queries forwarded (to your chosen upstream DNS server)
* Queries cached
* Unique clients
## Pi-hole™ Projects The API can be accessed via [`telnet`](https://github.com/pi-hole/FTL), the Web (`admin/api.php`) and Command Line (`pihole -c -j`). You can out find [more details over here](https://discourse.pi-hole.net/t/pi-hole-api/1863).
- [An ad blocking Magic Mirror](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware) -----
- [Pi-hole stats in your Mac's menu bar](https://getbitbar.com/plugins/Network/pi-hole.1m.py)
- [Get LED alerts for each blocked ad](http://thetimmy.silvernight.org/pages/endisbutton/) ## The Origin Of Pi-hole
- [Pi-hole on Ubuntu 14.04 on VirtualBox](http://hbalagtas.blogspot.com/2016/02/adblocking-with-pi-hole-and-ubuntu-1404.html) Pi-hole being a **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
* [`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.
-----
## Pi-hole Projects
- [The Big Blocklist Collection](https://wally3k.github.io)
- [Docker Pi-hole container (x86 and ARM)](https://hub.docker.com/r/diginc/pi-hole/) - [Docker Pi-hole container (x86 and ARM)](https://hub.docker.com/r/diginc/pi-hole/)
- [Pi-Hole in the cloud](http://blog.codybunch.com/2015/07/28/Pi-Hole-in-the-cloud/)
- [Pie in the Sky-Hole [A Pi-Hole in the cloud for ad-blocking via DNS]](https://dlaa.me/blog/post/skyhole)
- [Pi-hole Enable/Disable Button](http://thetimmy.silvernight.org/pages/endisbutton/)
- [Minibian Pi-hole](https://munkjensen.net/wiki/index.php/See_my_Pi-Hole#Minibian_Pi-hole)
- [CHiP-hole: Network-wide Ad-blocker](https://www.hackster.io/jacobsalmela/chip-hole-network-wide-ad-blocker-98e037)
- [Chrome Extension: Pi-Hole List Editor](https://chrome.google.com/webstore/detail/pi-hole-list-editor/hlnoeoejkllgkjbnnnhfolapllcnaglh) ([Source Code](https://github.com/packtloss/pihole-extension))
- [Splunk: Pi-hole Visualiser](https://splunkbase.splunk.com/app/3023/) - [Splunk: Pi-hole Visualiser](https://splunkbase.splunk.com/app/3023/)
- [Pi-hole Chrome extension](https://chrome.google.com/webstore/detail/pi-hole-list-editor/hlnoeoejkllgkjbnnnhfolapllcnaglh) ([open source](https://github.com/packtloss/pihole-extension)) - [Adblocking with P-hole and Ubuntu 14.04 on VirtualBox](https://hbalagtas.blogspot.com.au/2016/02/adblocking-with-pi-hole-and-ubuntu-1404.html)
- [Go Bananas for CHiP-hole ad blocking](https://www.hackster.io/jacobsalmela/chip-hole-network-wide-ad-blocker-98e037) - [Pi-hole stats in your Mac's menu bar](https://getbitbar.com/plugins/Network/pi-hole.1m.py)
- [Sky-Hole](http://dlaa.me/blog/post/skyhole) - [Pi-hole unRAID Template](https://forums.lime-technology.com/topic/36810-support-spants-nodered-mqtt-dashing-couchdb/)
- [Pi-hole in the Cloud!](http://blog.codybunch.com/2015/07/28/Pi-Hole-in-the-cloud/) - [Copernicus: Windows Tray Application](https://github.com/goldbattle/copernicus)
- [unRaid-hole](https://github.com/spants/unraidtemplates/blob/master/Spants/unRaid-hole.xml#L13)--[Repo and more info](http://lime-technology.com/forum/index.php?PHPSESSID=c0eae3e5ef7e521f7866034a3336489d&topic=38486.0)
- [Pi-hole on/off button](http://thetimmy.silvernight.org/pages/endisbutton/)
- [Minibian Pi-hole](http://munkjensen.net/wiki/index.php/See_my_Pi-Hole#Minibian_Pi-hole)
- [Windows Tray Stat Application](https://github.com/goldbattle/copernicus)
- [Let your blink1 device blink when Pi-hole filters ads](https://gist.github.com/elpatron68/ec0b4c582e5abf604885ac1e068d233f) - [Let your blink1 device blink when Pi-hole filters ads](https://gist.github.com/elpatron68/ec0b4c582e5abf604885ac1e068d233f)
- [Pi-hole Prometheus exporter](https://github.com/nlamirault/pihole_exporter): a [Prometheus](https://prometheus.io/) exporter for Pi-hole - [Pi-hole metrics](https://github.com/nlamirault/pihole_exporter) exporter for [Prometheus](https://prometheus.io/)
- [Pi-hole Droid - open source Android client](https://github.com/friimaind/pi-hole-droid) - [Magic Mirror with DNS Filtering](https://zonksec.com/blog/magic-mirror-dns-filtering/#dnssoftware)
- [Windows DNS Swapper](https://github.com/roots84/DNS-Swapper), see [#1400](https://github.com/pi-hole/pi-hole/issues/1400) - [Pi-hole Droid: Android client](https://github.com/friimaind/pi-hole-droid)
-----
## Coverage ## Coverage
- [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/)
- [Adafruit livestream install](https://www.youtube.com/watch?v=eg4u2j1HYlI) - [MakeUseOf: Adblock Everywhere: The Raspberry Pi-Hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/)
- [TekThing: 5 fun, easy projects for a Raspberry Pi](https://youtu.be/QwrKlyC2kdM?t=1m42s) - [Catchpoint: Ad-Blocking on Apple iOS9: Valuing the End User Experience](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/)
- [Pi-hole on Adafruit's blog](https://blog.adafruit.com/2016/03/04/pi-hole-is-a-black-hole-for-internet-ads-piday-raspberrypi-raspberry_pi/) - [Security Now Netcast: Pi-hole](https://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s)
- [The Defrag Show - MSDN/Channel 9](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) - [TekThing: Raspberry Pi-Hole Makes Ads Disappear!](https://youtu.be/8Co59HU2gY0?t=2m)
- [MacObserver Podcast 585](http://www.macobserver.com/tmo/podcast/macgeekgab-585)
- [Medium: Block All Ads For $53](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d)
- [MakeUseOf: Adblock Everywhere, The Pi-hole Way](http://www.makeuseof.com/tag/adblock-everywhere-raspberry-pi-hole-way/)
- [Lifehacker: Turn Your Pi Into An Ad Blocker With A Single Command](http://lifehacker.com/turn-a-raspberry-pi-into-an-ad-blocker-with-a-single-co-1686093533)!
- [Pi-hole on TekThing](https://youtu.be/8Co59HU2gY0?t=2m)
- [Pi-hole on Security Now! Podcast](http://www.youtube.com/watch?v=p7-osq_y8i8&t=100m26s)
- [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s) - [Foolish Tech Show](https://youtu.be/bYyena0I9yc?t=2m4s)
- [Pi-hole on Ubuntu](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/) - [Block Ads on All Home Devices for $53.18](https://medium.com/@robleathern/block-ads-on-all-home-devices-for-53-18-a5f1ec139693#.gj1xpgr5d)
- [Catchpoint: iOS 9 Ad Blocking](http://blog.catchpoint.com/2015/09/14/ad-blocking-apple/) - [Pi-Hole for Ubuntu 14.04](http://www.boyter.org/2015/12/pi-hole-ubuntu-14-04/)
- [Build an Ad-Blocker for less than 10$ with Orange-Pi](http://www.devacron.com/orangepi-zero-as-an-ad-block-server-with-pi-hole/) - [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)

View file

@ -21,8 +21,8 @@
############################################################################### ###############################################################################
addn-hosts=/etc/pihole/gravity.list addn-hosts=/etc/pihole/gravity.list
addn-hosts=/etc/pihole/local.list
addn-hosts=/etc/pihole/black.list addn-hosts=/etc/pihole/black.list
addn-hosts=/etc/pihole/local.list
domain-needed domain-needed
@ -42,6 +42,6 @@ cache-size=10000
log-queries log-queries
log-facility=/var/log/pihole.log log-facility=/var/log/pihole.log
local-ttl=300 local-ttl=2
log-async log-async

View file

@ -0,0 +1,49 @@
# Determine if terminal is capable of showing colours
if [[ -t 1 ]] && [[ $(tput colors) -ge 8 ]]; then
# Bold and underline may not show up on all clients
# If something MUST be emphasised, use both
COL_BOLD=''
COL_ULINE=''
COL_NC=''
COL_GRAY=''
COL_RED=''
COL_GREEN=''
COL_YELLOW=''
COL_BLUE=''
COL_PURPLE=''
COL_CYAN=''
else
# Provide empty variables for `set -u`
COL_BOLD=""
COL_ULINE=""
COL_NC=""
COL_GRAY=""
COL_RED=""
COL_GREEN=""
COL_YELLOW=""
COL_BLUE=""
COL_PURPLE=""
COL_CYAN=""
fi
# Deprecated variables
COL_WHITE="${COL_BOLD}"
COL_BLACK="${COL_NC}"
COL_LIGHT_BLUE="${COL_BLUE}"
COL_LIGHT_GREEN="${COL_GREEN}"
COL_LIGHT_CYAN="${COL_CYAN}"
COL_LIGHT_RED="${COL_RED}"
COL_URG_RED="${COL_RED}${COL_BOLD}${COL_ULINE}"
COL_LIGHT_PURPLE="${COL_PURPLE}"
COL_BROWN="${COL_YELLOW}"
COL_LIGHT_GRAY="${COL_GRAY}"
COL_DARK_GRAY="${COL_GRAY}"
TICK="[${COL_GREEN}✓${COL_NC}]"
CROSS="[${COL_RED}✗${COL_NC}]"
INFO="[i]"
QST="[?]"
DONE="${COL_GREEN} done!${COL_NC}"
OVER="\\r"

View file

@ -1,4 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1090,SC1091
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
@ -7,6 +8,7 @@
# #
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
LC_NUMERIC=C
# Retrieve stats from FTL engine # Retrieve stats from FTL engine
pihole-FTL() { pihole-FTL() {
@ -32,43 +34,74 @@ pihole-FTL() {
exec 3<&- exec 3<&-
fi fi
else else
echo -e "${COL_LIGHT_RED}FTL offline${COL_NC}" echo "0"
fi fi
} }
# Print spaces to align right-side content # Print spaces to align right-side additional text
printFunc() { printFunc() {
txt_len="${#2}" local text_last
# Reduce string length when using colour code title="$1"
[ "${2:0:1}" == "" ] && txt_len=$((txt_len-7)) title_len="${#title}"
if [[ "$3" == "last" ]]; then text_main="$2"
# Prevent final line from printing trailing newline text_main_nocol="$text_main"
scr_size=( $(stty size 2>/dev/null || echo 24 80) ) if [[ "${text_main:0:1}" == "" ]]; then
scr_width="${scr_size[1]}" text_main_nocol=$(sed 's/\[[0-9;]\{1,5\}m//g' <<< "$text_main")
fi
text_main_len="${#text_main_nocol}"
title_len="${#1}" text_addn="$3"
spc_num=$(( (scr_width - title_len) - txt_len )) if [[ "$text_addn" == "last" ]]; then
[[ "$spc_num" -lt 0 ]] && spc_num="0" text_addn=""
spc=$(printf "%${spc_num}s") text_last="true"
fi
printf "%s%s$spc" "$1" "$2" # If there is additional text, define max length of text_main
if [[ -n "$text_addn" ]]; then
case "$scr_cols" in
[0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-4]) text_main_max_len="9";;
4[5-9]) text_main_max_len="14";;
*) text_main_max_len="19";;
esac
fi
[[ -z "$text_addn" ]] && text_main_max_len="$(( scr_cols - title_len ))"
# Remove excess characters from main text
if [[ "$text_main_len" -gt "$text_main_max_len" ]]; then
# Trim text without colours
text_main_trim="${text_main_nocol:0:$text_main_max_len}"
# Replace with trimmed text
text_main="${text_main/$text_main_nocol/$text_main_trim}"
fi
# Determine amount of spaces for each line
if [[ -n "$text_last" ]]; then
# Move cursor to end of screen
spc_num=$(( scr_cols - ( title_len + text_main_len ) ))
else else
# Determine number of spaces for padding spc_num=$(( text_main_max_len - text_main_len ))
spc_num=$(( 20 - txt_len )) fi
[[ "$spc_num" -lt 0 ]] && spc_num="0"
spc=$(printf "%${spc_num}s")
# Print string (Max 20 characters, prevents overflow) [[ "$spc_num" -le 0 ]] && spc_num="0"
printf "%s%s$spc" "$1" "${2:0:20}" spc=$(printf "%${spc_num}s")
#spc="${spc// /.}" # Debug: Visualise spaces
printf "%s%s$spc" "$title" "$text_main"
if [[ -n "$text_addn" ]]; then
printf "%s(%s)%s\\n" "$COL_NC$COL_DARK_GRAY" "$text_addn" "$COL_NC"
else
# Do not print trailing newline on final line
[[ -z "$text_last" ]] && printf "%s\\n" "$COL_NC"
fi fi
} }
# Perform on first Chrono run (not for JSON formatted string) # Perform on first Chrono run (not for JSON formatted string)
get_init_stats() { get_init_stats() {
LC_NUMERIC=C calcFunc(){ awk "BEGIN {print $*}" 2> /dev/null; }
calcFunc(){ awk "BEGIN {print $*}"; }
# Convert bytes to human-readable format # Convert bytes to human-readable format
hrBytes() { hrBytes() {
@ -90,10 +123,11 @@ get_init_stats() {
# Convert seconds to human-readable format # Convert seconds to human-readable format
hrSecs() { hrSecs() {
day=$(( $1/60/60/24 )); hrs=$(( $1/3600%24 )); mins=$(( ($1%3600)/60 )); secs=$(( $1%60 )) day=$(( $1/60/60/24 )); hrs=$(( $1/3600%24 ))
mins=$(( ($1%3600)/60 )); secs=$(( $1%60 ))
[[ "$day" -ge "2" ]] && plu="s" [[ "$day" -ge "2" ]] && plu="s"
[[ "$day" -ge "1" ]] && days="$day day${plu}, " || days="" [[ "$day" -ge "1" ]] && days="$day day${plu}, " || days=""
printf "%s%02d:%02d:%02d\n" "$days" "$hrs" "$mins" "$secs" printf "%s%02d:%02d:%02d\\n" "$days" "$hrs" "$mins" "$secs"
} }
# Set Colour Codes # Set Colour Codes
@ -101,20 +135,39 @@ get_init_stats() {
if [[ -f "${coltable}" ]]; then if [[ -f "${coltable}" ]]; then
source ${coltable} source ${coltable}
else else
COL_NC='' COL_NC=""
COL_DARK_GRAY='' COL_DARK_GRAY=""
COL_LIGHT_GREEN='' COL_LIGHT_GREEN=""
COL_LIGHT_BLUE='' COL_LIGHT_BLUE=""
COL_LIGHT_RED='' COL_LIGHT_RED=""
COL_YELLOW='' COL_YELLOW=""
COL_LIGHT_RED='' COL_LIGHT_RED=""
COL_URG_RED='' COL_URG_RED=""
fi fi
# Get RPi model number, or OS distro info # Get RPi throttle state (RPi 3B only) & model number, or OS distro info
if command -v vcgencmd &> /dev/null; then if command -v vcgencmd &> /dev/null; then
sys_rev=$(awk '/Revision/ {print $3}' < /proc/cpuinfo) local sys_throttle_raw
case "$sys_rev" in local sys_rev_raw
sys_throttle_raw=$(vgt=$(sudo vcgencmd get_throttled); echo "${vgt##*x}")
# Active Throttle Notice: http://bit.ly/2gnunOo
if [[ "$sys_throttle_raw" != "0" ]]; then
case "$sys_throttle_raw" in
*0001) thr_type="${COL_YELLOW}Under Voltage";;
*0002) thr_type="${COL_LIGHT_BLUE}Arm Freq Cap";;
*0003) thr_type="${COL_YELLOW}UV${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_BLUE}AFC";;
*0004) thr_type="${COL_LIGHT_RED}Throttled";;
*0005) thr_type="${COL_YELLOW}UV${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_RED}TT";;
*0006) thr_type="${COL_LIGHT_BLUE}AFC${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_RED}TT";;
*0007) thr_type="${COL_YELLOW}UV${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_BLUE}AFC${COL_DARK_GRAY},${COL_NC} ${COL_LIGHT_RED}TT";;
esac
[[ -n "$thr_type" ]] && sys_throttle="$thr_type${COL_DARK_GRAY}"
fi
sys_rev_raw=$(awk '/Revision/ {print $3}' < /proc/cpuinfo)
case "$sys_rev_raw" in
000[2-6]) sys_model=" 1, Model B";; # 256MB 000[2-6]) sys_model=" 1, Model B";; # 256MB
000[7-9]) sys_model=" 1, Model A";; # 256MB 000[7-9]) sys_model=" 1, Model A";; # 256MB
000d|000e|000f) sys_model=" 1, Model B";; # 512MB 000d|000e|000f) sys_model=" 1, Model B";; # 512MB
@ -137,7 +190,6 @@ get_init_stats() {
# Get core count # Get core count
sys_cores=$(grep -c "^processor" /proc/cpuinfo) sys_cores=$(grep -c "^processor" /proc/cpuinfo)
[[ "$sys_cores" -ne 1 ]] && sys_cores_plu="cores" || sys_cores_plu="core"
# Test existence of clock speed file for ARM CPU # Test existence of clock speed file for ARM CPU
if [[ -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then if [[ -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then
@ -168,16 +220,16 @@ get_sys_stats() {
# Update every 12 refreshes (Def: every 60s) # Update every 12 refreshes (Def: every 60s)
count=$((count+1)) count=$((count+1))
if [[ "$count" == "1" ]] || (( "$count" % 12 == 0 )); then if [[ "$count" == "1" ]] || (( "$count" % 12 == 0 )); then
# Do not source setupVars if file does not exist
[[ -n "$setupVars" ]] && source "$setupVars" [[ -n "$setupVars" ]] && source "$setupVars"
mapfile -t ph_ver_raw < <(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p')
ph_ver_raw=($(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p'))
if [[ -n "${ph_ver_raw[0]}" ]]; then if [[ -n "${ph_ver_raw[0]}" ]]; then
ph_core_ver="${ph_ver_raw[0]}" ph_core_ver="${ph_ver_raw[0]}"
ph_lte_ver="${ph_ver_raw[1]}" ph_lte_ver="${ph_ver_raw[1]}"
ph_ftl_ver="${ph_ver_raw[2]}" ph_ftl_ver="${ph_ver_raw[2]}"
else else
ph_core_ver="${COL_LIGHT_RED}API unavailable${COL_NC}" ph_core_ver="-1"
fi fi
sys_name=$(hostname) sys_name=$(hostname)
@ -185,7 +237,7 @@ get_sys_stats() {
[[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c" [[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c"
# Get storage stats for partition mounted on / # Get storage stats for partition mounted on /
disk_raw=($(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }')) read -r -a disk_raw <<< "$(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }')"
disk_used="${disk_raw[0]}" disk_used="${disk_raw[0]}"
disk_total="${disk_raw[1]}" disk_total="${disk_raw[1]}"
disk_perc="${disk_raw[2]}" disk_perc="${disk_raw[2]}"
@ -194,15 +246,12 @@ get_sys_stats() {
# Get DHCP stats, if feature is enabled # Get DHCP stats, if feature is enabled
if [[ "$DHCP_ACTIVE" == "true" ]]; then if [[ "$DHCP_ACTIVE" == "true" ]]; then
ph_dhcp_eip="${DHCP_END##*.}"
ph_dhcp_max=$(( ${DHCP_END##*.} - ${DHCP_START##*.} + 1 )) ph_dhcp_max=$(( ${DHCP_END##*.} - ${DHCP_START##*.} + 1 ))
fi fi
# Get alt DNS server, or print total count of alt DNS servers # Get DNS server count
if [[ -z "${PIHOLE_DNS_3}" ]]; then
ph_alts="${PIHOLE_DNS_2}"
else
dns_count="0" dns_count="0"
[[ -n "${PIHOLE_DNS_1}" ]] && dns_count=$((dns_count+1))
[[ -n "${PIHOLE_DNS_2}" ]] && dns_count=$((dns_count+1)) [[ -n "${PIHOLE_DNS_2}" ]] && dns_count=$((dns_count+1))
[[ -n "${PIHOLE_DNS_3}" ]] && dns_count=$((dns_count+1)) [[ -n "${PIHOLE_DNS_3}" ]] && dns_count=$((dns_count+1))
[[ -n "${PIHOLE_DNS_4}" ]] && dns_count=$((dns_count+1)) [[ -n "${PIHOLE_DNS_4}" ]] && dns_count=$((dns_count+1))
@ -211,14 +260,33 @@ get_sys_stats() {
[[ -n "${PIHOLE_DNS_7}" ]] && dns_count=$((dns_count+1)) [[ -n "${PIHOLE_DNS_7}" ]] && dns_count=$((dns_count+1))
[[ -n "${PIHOLE_DNS_8}" ]] && dns_count=$((dns_count+1)) [[ -n "${PIHOLE_DNS_8}" ]] && dns_count=$((dns_count+1))
[[ -n "${PIHOLE_DNS_9}" ]] && dns_count="$dns_count+" [[ -n "${PIHOLE_DNS_9}" ]] && dns_count="$dns_count+"
ph_alts="${dns_count} others"
fi fi
# Get screen size
read -r -a scr_size <<< "$(stty size 2>/dev/null || echo 24 80)"
scr_lines="${scr_size[0]}"
scr_cols="${scr_size[1]}"
# Determine Chronometer size behaviour
if [[ "$scr_cols" -ge 58 ]]; then
chrono_width="large"
elif [[ "$scr_cols" -gt 40 ]]; then
chrono_width="medium"
else
chrono_width="small"
fi fi
# Determine max length of divider string
scr_line_len=$(( scr_cols - 2 ))
[[ "$scr_line_len" -ge 58 ]] && scr_line_len="58"
scr_line_str=$(printf "%${scr_line_len}s")
scr_line_str="${scr_line_str// /—}"
sys_uptime=$(hrSecs "$(cut -d. -f1 /proc/uptime)") sys_uptime=$(hrSecs "$(cut -d. -f1 /proc/uptime)")
sys_loadavg=$(cut -d " " -f1,2,3 /proc/loadavg) sys_loadavg=$(cut -d " " -f1,2,3 /proc/loadavg)
# Get CPU usage, only counting processes over 1% CPU as active # Get CPU usage, only counting processes over 1% as active
# shellcheck disable=SC2009
cpu_raw=$(ps -eo pcpu,rss --no-headers | grep -E -v " 0") cpu_raw=$(ps -eo pcpu,rss --no-headers | grep -E -v " 0")
cpu_tasks=$(wc -l <<< "$cpu_raw") cpu_tasks=$(wc -l <<< "$cpu_raw")
cpu_taskact=$(sed -r "/(^ 0.)/d" <<< "$cpu_raw" | wc -l) cpu_taskact=$(sed -r "/(^ 0.)/d" <<< "$cpu_raw" | wc -l)
@ -228,19 +296,20 @@ get_sys_stats() {
if [[ -n "$scaling_freq_file" ]]; then if [[ -n "$scaling_freq_file" ]]; then
cpu_mhz=$(( $(< /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) / 1000 )) cpu_mhz=$(( $(< /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) / 1000 ))
else else
cpu_mhz=$(lscpu | awk -F "[ .]+" '/MHz/ {print $4;exit}') cpu_mhz=$(lscpu | awk -F ":" '/MHz/ {print $2;exit}')
cpu_mhz=$(printf "%.0f" "${cpu_mhz//[[:space:]]/}")
fi fi
# Determine correct string format for CPU clock speed # Determine whether to display CPU clock speed as MHz or GHz
if [[ -n "$cpu_mhz" ]]; then if [[ -n "$cpu_mhz" ]]; then
[[ "$cpu_mhz" -le "999" ]] && cpu_freq="$cpu_mhz MHz" || cpu_freq="$(calcFunc "$cpu_mhz"/1000) Ghz" [[ "$cpu_mhz" -le "999" ]] && cpu_freq="$cpu_mhz MHz" || cpu_freq="$(printf "%.1f" $(calcFunc "$cpu_mhz"/1000)) GHz"
[[ -n "$cpu_freq" ]] && cpu_freq_str=" @ $cpu_freq" || cpu_freq_str="" [[ "${cpu_freq}" == *".0"* ]] && cpu_freq="${cpu_freq/.0/}"
fi fi
# Determine colour for temperature # Determine colour for temperature
if [[ -n "$temp_file" ]]; then if [[ -n "$temp_file" ]]; then
if [[ "$temp_unit" == "C" ]]; then if [[ "$temp_unit" == "C" ]]; then
cpu_temp=$(printf "%'.0fc\n" "$(calcFunc "$(< $temp_file) / 1000")") cpu_temp=$(printf "%.0fc\\n" "$(calcFunc "$(< $temp_file) / 1000")")
case "${cpu_temp::-1}" in case "${cpu_temp::-1}" in
-*|[0-9]|[1-3][0-9]) cpu_col="$COL_LIGHT_BLUE";; -*|[0-9]|[1-3][0-9]) cpu_col="$COL_LIGHT_BLUE";;
@ -251,10 +320,10 @@ get_sys_stats() {
esac esac
# $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED # $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED
cpu_temp_str=", $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
elif [[ "$temp_unit" == "F" ]]; then elif [[ "$temp_unit" == "F" ]]; then
cpu_temp=$(printf "%'.0ff\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")") cpu_temp=$(printf "%.0ff\\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")")
case "${cpu_temp::-1}" in case "${cpu_temp::-1}" in
-*|[0-9]|[0-9][0-9]) cpu_col="$COL_LIGHT_BLUE";; -*|[0-9]|[0-9][0-9]) cpu_col="$COL_LIGHT_BLUE";;
@ -264,16 +333,16 @@ get_sys_stats() {
*) cpu_col="$COL_URG_RED";; *) cpu_col="$COL_URG_RED";;
esac esac
cpu_temp_str=", $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY"
else else
cpu_temp_str=$(printf ", %'.0fk\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")") cpu_temp_str=$(printf " @ %.0fk\\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")")
fi fi
else else
cpu_temp_str="" cpu_temp_str=""
fi fi
ram_raw=($(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.0f %.0f %.0f", (total-free-buffers-cached)*100/total, (total-free-buffers-cached)*1024, total*1024}' /proc/meminfo)) read -r -a ram_raw <<< "$(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.0f %.0f %.0f", (total-free-buffers-cached)*100/total, (total-free-buffers-cached)*1024, total*1024}' /proc/meminfo)"
ram_perc="${ram_raw[0]}" ram_perc="${ram_raw[0]}"
ram_used="${ram_raw[1]}" ram_used="${ram_raw[1]}"
ram_total="${ram_raw[2]}" ram_total="${ram_raw[2]}"
@ -281,46 +350,95 @@ get_sys_stats() {
if [[ "$(pihole status web 2> /dev/null)" == "1" ]]; then if [[ "$(pihole status web 2> /dev/null)" == "1" ]]; then
ph_status="${COL_LIGHT_GREEN}Active" ph_status="${COL_LIGHT_GREEN}Active"
else else
ph_status="${COL_LIGHT_RED}Inactive" ph_status="${COL_LIGHT_RED}Offline"
fi fi
if [[ "$DHCP_ACTIVE" == "true" ]]; then if [[ "$DHCP_ACTIVE" == "true" ]]; then
ph_dhcp_num=$(wc -l 2> /dev/null < "/etc/pihole/dhcp.leases") local ph_dhcp_range
ph_dhcp_range=$(seq -s "|" -f "${DHCP_START%.*}.%g" "${DHCP_START##*.}" "${DHCP_END##*.}")
# Count dynamic leases from available range, and not static leases
ph_dhcp_num=$(grep -cE "$ph_dhcp_range" "/etc/pihole/dhcp.leases")
ph_dhcp_percent=$(( ph_dhcp_num * 100 / ph_dhcp_max ))
fi fi
} }
get_ftl_stats() { get_ftl_stats() {
local stats_raw local stats_raw
stats_raw=($(pihole-FTL "stats")) mapfile -t stats_raw < <(pihole-FTL "stats")
domains_being_blocked_raw="${stats_raw[1]}" domains_being_blocked_raw="${stats_raw[0]#* }"
dns_queries_today_raw="${stats_raw[3]}" dns_queries_today_raw="${stats_raw[1]#* }"
ads_blocked_today_raw="${stats_raw[5]}" ads_blocked_today_raw="${stats_raw[2]#* }"
ads_percentage_today_raw="${stats_raw[7]}" ads_percentage_today_raw="${stats_raw[3]#* }"
queries_forwarded_raw="${stats_raw[5]#* }"
queries_cached_raw="${stats_raw[6]#* }"
# Only retrieve these stats when not called from jsonFunc # Only retrieve these stats when not called from jsonFunc
if [[ -z "$1" ]]; then if [[ -z "$1" ]]; then
local recent_blocked_raw
local top_ad_raw local top_ad_raw
local top_domain_raw local top_domain_raw
local top_client_raw local top_client_raw
domains_being_blocked=$(printf "%'.0f\n" "${domains_being_blocked_raw}") domains_being_blocked=$(printf "%.0f\\n" "${domains_being_blocked_raw}" 2> /dev/null)
dns_queries_today=$(printf "%'.0f\n" "${dns_queries_today_raw}") dns_queries_today=$(printf "%.0f\\n" "${dns_queries_today_raw}")
ads_blocked_today=$(printf "%'.0f\n" "${ads_blocked_today_raw}") ads_blocked_today=$(printf "%.0f\\n" "${ads_blocked_today_raw}")
ads_percentage_today=$(printf "%'.0f\n" "${ads_percentage_today_raw}") ads_percentage_today=$(printf "%'.0f\\n" "${ads_percentage_today_raw}")
queries_cached_percentage=$(printf "%.0f\\n" "$(calcFunc "$queries_cached_raw * 100 / ( $queries_forwarded_raw + $queries_cached_raw )")")
recent_blocked=$(pihole-FTL recentBlocked)
read -r -a top_ad_raw <<< "$(pihole-FTL "top-ads (1)")"
read -r -a top_domain_raw <<< "$(pihole-FTL "top-domains (1)")"
read -r -a top_client_raw <<< "$(pihole-FTL "top-clients (1)")"
recent_blocked_raw=$(pihole-FTL recentBlocked) top_ad="${top_ad_raw[2]}"
top_ad_raw=($(pihole-FTL "top-ads (1)")) top_domain="${top_domain_raw[2]}"
top_domain_raw=($(pihole-FTL "top-domains (1)")) if [[ "${top_client_raw[3]}" ]]; then
top_client_raw=($(pihole-FTL "top-clients (1)")) top_client="${top_client_raw[3]}"
else
# Limit strings to 40 characters to prevent overflow top_client="${top_client_raw[2]}"
recent_blocked="${recent_blocked_raw:0:40}"
top_ad="${top_ad_raw[2]:0:40}"
top_domain="${top_domain_raw[2]:0:40}"
[[ "${top_client_raw[3]}" ]] && top_client="${top_client_raw[3]:0:40}" || top_client="${top_client_raw[2]:0:40}"
fi fi
fi
}
get_strings() {
# Expand or contract strings depending on screen size
if [[ "$chrono_width" == "large" ]]; then
phc_str=" ${COL_DARK_GRAY}Core"
lte_str=" ${COL_DARK_GRAY}Web"
ftl_str=" ${COL_DARK_GRAY}FTL"
api_str="${COL_LIGHT_RED}API Offline"
host_info="$sys_type"
sys_info="$sys_throttle"
sys_info2="Active: $cpu_taskact of $cpu_tasks tasks"
used_str="Used: "
leased_str="Leased: "
domains_being_blocked=$(printf "%'.0f" "$domains_being_blocked")
ads_blocked_today=$(printf "%'.0f" "$ads_blocked_today")
dns_queries_today=$(printf "%'.0f" "$dns_queries_today")
ph_info="Blocking: $domains_being_blocked sites"
total_str="Total: "
else
phc_str=" ${COL_DARK_GRAY}Core"
lte_str=" ${COL_DARK_GRAY}Web"
ftl_str=" ${COL_DARK_GRAY}FTL"
api_str="${COL_LIGHT_RED}API Down"
ph_info="$domains_being_blocked blocked"
fi
[[ "$sys_cores" -ne 1 ]] && sys_cores_txt="${sys_cores}x "
cpu_info="$sys_cores_txt$cpu_freq$cpu_temp_str"
ram_info="$used_str$(hrBytes "$ram_used") of $(hrBytes "$ram_total")"
disk_info="$used_str$(hrBytes "$disk_used") of $(hrBytes "$disk_total")"
lan_info="Gateway: $net_gateway"
dhcp_info="$leased_str$ph_dhcp_num of $ph_dhcp_max"
ads_info="$total_str$ads_blocked_today of $dns_queries_today"
dns_info="$dns_count DNS servers"
[[ "$recent_blocked" == "0" ]] && recent_blocked="${COL_LIGHT_RED}FTL offline${COL_NC}"
} }
chronoFunc() { chronoFunc() {
@ -329,66 +447,86 @@ chronoFunc() {
for (( ; ; )); do for (( ; ; )); do
get_sys_stats get_sys_stats
get_ftl_stats get_ftl_stats
get_strings
# Do not print LTE/FTL strings if API is unavailable # Strip excess development version numbers
ph_core_str=" ${COL_DARK_GRAY}Pi-hole: $ph_core_ver${COL_NC}" if [[ "$ph_core_ver" != "-1" ]]; then
if [[ -n "$ph_lte_ver" ]]; then phc_ver_str="$phc_str: ${ph_core_ver%-*}${COL_NC}"
ph_lte_str=" ${COL_DARK_GRAY}AdminLTE: $ph_lte_ver${COL_NC}" lte_ver_str="$lte_str: ${ph_lte_ver%-*}${COL_NC}"
ph_ftl_str=" ${COL_DARK_GRAY}FTL: $ph_ftl_ver${COL_NC}" ftl_ver_str="$ftl_str: ${ph_ftl_ver%-*}${COL_NC}"
else
phc_ver_str="$phc_str: $api_str${COL_NC}"
fi
# Get refresh number
if [[ "$*" == *"-r"* ]]; then
num="$*"
num="${num/*-r /}"
num="${num/ */}"
num_str="Refresh set for every $num seconds"
else
num_str=""
fi fi
clear clear
echo -e "|¯¯¯(¯)__|¯|_ ___|¯|___$ph_core_str # Remove exit message heading on third refresh
| ¯_/¯|__| ' \/ _ \ / -_)$ph_lte_str if [[ "$count" -le 2 ]] && [[ "$*" != *"-e"* ]]; then
|_| |_| |_||_\___/_\___|$ph_ftl_str echo -e " ${COL_LIGHT_GREEN}Pi-hole Chronometer${COL_NC}
${COL_DARK_GRAY}——————————————————————————————————————————————————————————${COL_NC}" $num_str
${COL_LIGHT_RED}Press Ctrl-C to exit${COL_NC}
printFunc " Hostname: " "$sys_name" ${COL_DARK_GRAY}$scr_line_str${COL_NC}"
[ -n "$sys_type" ] && printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$sys_type" "$COL_NC" || printf "\n" else
echo -e "|¯¯¯(¯)_|¯|_ ___|¯|___$phc_ver_str
printf "%s\n" " Uptime: $sys_uptime" | ¯_/¯|_| ' \\/ _ \\ / -_)$lte_ver_str
|_| |_| |_||_\\___/_\\___|$ftl_ver_str
printFunc " Task Load: " "$sys_loadavg" ${COL_DARK_GRAY}$scr_line_str${COL_NC}"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Active: $cpu_taskact of $cpu_tasks tasks" "$COL_NC"
printFunc " CPU usage: " "$cpu_perc%"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$sys_cores $sys_cores_plu$cpu_freq_str$cpu_temp_str" "$COL_NC"
printFunc " RAM usage: " "$ram_perc%"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Used: $(hrBytes "$ram_used") of $(hrBytes "$ram_total")" "$COL_NC"
printFunc " HDD usage: " "$disk_perc"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Used: $(hrBytes "$disk_used") of $(hrBytes "$disk_total")" "$COL_NC"
printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Gateway: $net_gateway" "$COL_NC"
if [[ "$DHCP_ACTIVE" == "true" ]]; then
printFunc " DHCP: " "$DHCP_START to $ph_dhcp_eip"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Leased: $ph_dhcp_num of $ph_dhcp_max" "$COL_NC"
fi fi
printFunc " Pi-hole: " "$ph_status" printFunc " Hostname: " "$sys_name" "$host_info"
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Blocking: $domains_being_blocked sites" "$COL_NC" printFunc " Uptime: " "$sys_uptime" "$sys_info"
printFunc " Task Load: " "$sys_loadavg" "$sys_info2"
printFunc " CPU usage: " "$cpu_perc%" "$cpu_info"
printFunc " RAM usage: " "$ram_perc%" "$ram_info"
printFunc " HDD usage: " "$disk_perc" "$disk_info"
printFunc " Ads Today: " "$ads_percentage_today%" if [[ "$scr_lines" -gt 17 ]] && [[ "$chrono_width" != "small" ]]; then
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$ads_blocked_today of $dns_queries_today queries" "$COL_NC" printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}" "$lan_info"
fi
printFunc " Fwd DNS: " "$PIHOLE_DNS_1" if [[ "$DHCP_ACTIVE" == "true" ]]; then
printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Alt DNS: $ph_alts" "$COL_NC" printFunc "DHCP usage: " "$ph_dhcp_percent%" "$dhcp_info"
fi
echo -e " ${COL_DARK_GRAY}——————————————————————————————————————————————————————————${COL_NC}" printFunc " Pi-hole: " "$ph_status" "$ph_info"
echo " Recently blocked: $recent_blocked" printFunc " Ads Today: " "$ads_percentage_today%" "$ads_info"
echo " Top Advertiser: $top_ad" printFunc "Local Qrys: " "$queries_cached_percentage%" "$dns_info"
echo " Top Domain: $top_domain"
printFunc " Blocked: " "$recent_blocked"
printFunc "Top Advert: " "$top_ad"
# Provide more stats on screens with more lines
if [[ "$scr_lines" -eq 17 ]]; then
if [[ "$DHCP_ACTIVE" == "true" ]]; then
printFunc "Top Domain: " "$top_domain" "last"
else
print_client="true"
fi
else
print_client="true"
fi
if [[ -n "$print_client" ]]; then
printFunc "Top Domain: " "$top_domain"
printFunc "Top Client: " "$top_client" "last" printFunc "Top Client: " "$top_client" "last"
fi
if [[ "$1" == "exit" ]]; then # Handle exit/refresh options
if [[ "$*" == *"-e"* ]]; then
exit 0 exit 0
else else
if [[ -n "$1" ]]; then if [[ "$*" == *"-r"* ]]; then
sleep "${1}" sleep "$num"
else else
sleep 5 sleep 5
fi fi
@ -428,8 +566,8 @@ for var in "$@"; do
case "$var" in case "$var" in
"-j" | "--json" ) jsonFunc;; "-j" | "--json" ) jsonFunc;;
"-h" | "--help" ) helpFunc;; "-h" | "--help" ) helpFunc;;
"-r" | "--refresh" ) chronoFunc "$2";; "-r" | "--refresh" ) chronoFunc "$@";;
"-e" | "--exit" ) chronoFunc "exit";; "-e" | "--exit" ) chronoFunc "$@";;
* ) helpFunc "?";; * ) helpFunc "?";;
esac esac
done done

View file

@ -19,11 +19,14 @@ addmode=true
verbose=true verbose=true
domList=() domList=()
domToRemoveList=()
listMain="" listMain=""
listAlt="" listAlt=""
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
helpFunc() { helpFunc() {
if [[ "${listMain}" == "${whitelist}" ]]; then if [[ "${listMain}" == "${whitelist}" ]]; then
param="w" param="w"
@ -45,7 +48,8 @@ Options:
-nr, --noreload Update ${type}list without refreshing dnsmasq -nr, --noreload Update ${type}list without refreshing dnsmasq
-q, --quiet Make output less verbose -q, --quiet Make output less verbose
-h, --help Show this help dialog -h, --help Show this help dialog
-l, --list Display all your ${type}listed domains" -l, --list Display all your ${type}listed domains
--nuke Removes all entries in a list"
exit 0 exit 0
} }
@ -58,15 +62,19 @@ EscapeRegexp() {
} }
HandleOther() { HandleOther() {
# First, convert everything to lowercase # Convert to lowercase
domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1") domain="${1,,}"
# Check validity of domain # Check validity of domain
validDomain=$(echo "${domain}" | perl -lne 'print if /(?!.*[^a-z0-9-\.].*)^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+\.)*[a-z]{2,63}/') if [[ "${#domain}" -le 253 ]]; then
if [[ -z "${validDomain}" ]]; then validDomain=$(grep -P "^((-|_)*[a-z\d]((-|_)*[a-z\d])*(-|_)*)(\.(-|_)*([a-z\d]((-|_)*[a-z\d])*))*$" <<< "${domain}") # Valid chars check
echo "::: $1 is not a valid argument or domain name" validDomain=$(grep -P "^[^\.]{1,63}(\.[^\.]{1,63})*$" <<< "${validDomain}") # Length of each label
else fi
if [[ -n "${validDomain}" ]]; then
domList=("${domList[@]}" ${validDomain}) domList=("${domList[@]}" ${validDomain})
else
echo -e " ${CROSS} ${domain} is not a valid argument or domain name!"
fi fi
} }
@ -94,7 +102,13 @@ AddDomain() {
list="$2" list="$2"
domain=$(EscapeRegexp "$1") domain=$(EscapeRegexp "$1")
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
[[ "${list}" == "${wildcardlist}" ]] && listname="wildcard blacklist"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
bool=true bool=true
# Is the domain in the list we want to add it to? # Is the domain in the list we want to add it to?
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
@ -102,38 +116,38 @@ AddDomain() {
if [[ "${bool}" == false ]]; then if [[ "${bool}" == false ]]; then
# Domain not found in the whitelist file, add it! # Domain not found in the whitelist file, add it!
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo "::: Adding $1 to $list..." echo -e " ${INFO} Adding $1 to $listname..."
fi fi
reload=true reload=true
# Add it to the list we want to add it to # Add it to the list we want to add it to
echo "$1" >> "${list}" echo "$1" >> "${list}"
else else
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo "::: ${1} already exists in ${list}, no need to add!" echo -e " ${INFO} ${1} already exists in ${listname}, no need to add!"
fi fi
fi fi
elif [[ "${list}" == "${wildcardlist}" ]]; then elif [[ "${list}" == "${wildcardlist}" ]]; then
source "${piholeDir}/setupVars.conf" source "${piholeDir}/setupVars.conf"
# Remove the /* from the end of the IPv4addr. # Remove the /* from the end of the IP addresses
IPV4_ADDRESS=${IPV4_ADDRESS%/*} IPV4_ADDRESS=${IPV4_ADDRESS%/*}
IPV6_ADDRESS=${IPV6_ADDRESS} IPV6_ADDRESS=${IPV6_ADDRESS%/*}
[[ -z "${type}" ]] && type="--wildcard-only"
bool=true bool=true
# Is the domain in the list? # Is the domain in the list?
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == false ]]; then if [[ "${bool}" == false ]]; then
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo "::: Adding $1 to wildcard blacklist..." echo -e " ${INFO} Adding $1 to wildcard blacklist..."
fi fi
reload=true reload="restart"
echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}" echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}"
if [[ "${#IPV6_ADDRESS}" > 0 ]]; then if [[ "${#IPV6_ADDRESS}" > 0 ]]; then
echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}" echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}"
fi fi
else else
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo "::: ${1} already exists in wildcard blacklist, no need to add!" echo -e " ${INFO} ${1} already exists in wildcard blacklist, no need to add!"
fi fi
fi fi
fi fi
@ -143,45 +157,54 @@ RemoveDomain() {
list="$2" list="$2"
domain=$(EscapeRegexp "$1") domain=$(EscapeRegexp "$1")
[[ "${list}" == "${whitelist}" ]] && listname="whitelist"
[[ "${list}" == "${blacklist}" ]] && listname="blacklist"
[[ "${list}" == "${wildcardlist}" ]] && listname="wildcard blacklist"
if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then
bool=true bool=true
[[ "${list}" == "${whitelist}" && -z "${type}" ]] && type="--whitelist-only"
[[ "${list}" == "${blacklist}" && -z "${type}" ]] && type="--blacklist-only"
# Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa # Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa
grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then if [[ "${bool}" == true ]]; then
# Remove it from the other one # Remove it from the other one
echo "::: Removing $1 from $list..." echo -e " ${INFO} Removing $1 from $listname..."
# /I flag: search case-insensitive # /I flag: search case-insensitive
sed -i "/${domain}/Id" "${list}" sed -i "/${domain}/Id" "${list}"
reload=true reload=true
else else
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo "::: ${1} does not exist in ${list}, no need to remove!" echo -e " ${INFO} ${1} does not exist in ${listname}, no need to remove!"
fi fi
fi fi
elif [[ "${list}" == "${wildcardlist}" ]]; then elif [[ "${list}" == "${wildcardlist}" ]]; then
[[ -z "${type}" ]] && type="--wildcard-only"
bool=true bool=true
# Is it in the list? # Is it in the list?
grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false
if [[ "${bool}" == true ]]; then if [[ "${bool}" == true ]]; then
# Remove it from the other one # Remove it from the other one
echo "::: Removing $1 from $list..." echo -e " ${INFO} Removing $1 from $listname..."
# /I flag: search case-insensitive # /I flag: search case-insensitive
sed -i "/address=\/${domain}/Id" "${list}" sed -i "/address=\/${domain}/Id" "${list}"
reload=true reload=true
else else
if [[ "${verbose}" == true ]]; then if [[ "${verbose}" == true ]]; then
echo "::: ${1} does not exist in ${list}, no need to remove!" echo -e " ${INFO} ${1} does not exist in ${listname}, no need to remove!"
fi fi
fi fi
fi fi
} }
# Update Gravity
Reload() { Reload() {
# Reload hosts file echo ""
pihole -g -sd pihole -g --skip-download "${type:-}"
} }
Displaylist() { Displaylist() {
if [[ -f ${listMain} ]]; then
if [[ "${listMain}" == "${whitelist}" ]]; then if [[ "${listMain}" == "${whitelist}" ]]; then
string="gravity resistant domains" string="gravity resistant domains"
else else
@ -194,9 +217,21 @@ Displaylist() {
echo " ${count}: ${RD}" echo " ${count}: ${RD}"
count=$((count+1)) count=$((count+1))
done < "${listMain}" done < "${listMain}"
else
echo -e " ${COL_LIGHT_RED}${listMain} does not exist!${COL_NC}"
fi
exit 0; exit 0;
} }
NukeList() {
if [[ -f "${listMain}" ]]; then
# Back up original list
cp "${listMain}" "${listMain}.bck~"
# Empty out file
echo "" > "${listMain}"
fi
}
for var in "$@"; do for var in "$@"; do
case "${var}" in case "${var}" in
"-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";; "-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";;
@ -204,10 +239,10 @@ for var in "$@"; do
"-wild" | "wildcard" ) listMain="${wildcardlist}";; "-wild" | "wildcard" ) listMain="${wildcardlist}";;
"-nr"| "--noreload" ) reload=false;; "-nr"| "--noreload" ) reload=false;;
"-d" | "--delmode" ) addmode=false;; "-d" | "--delmode" ) addmode=false;;
"-f" | "--force" ) force=true;;
"-q" | "--quiet" ) verbose=false;; "-q" | "--quiet" ) verbose=false;;
"-h" | "--help" ) helpFunc;; "-h" | "--help" ) helpFunc;;
"-l" | "--list" ) Displaylist;; "-l" | "--list" ) Displaylist;;
"--nuke" ) NukeList;;
* ) HandleOther "${var}";; * ) HandleOther "${var}";;
esac esac
done done
@ -220,6 +255,7 @@ fi
PoplistFile PoplistFile
if ${reload}; then if [[ "${reload}" != false ]]; then
Reload # Ensure that "restart" is used for Wildcard updates
Reload "${reload}"
fi fi

View file

@ -3,13 +3,14 @@
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
# #
# Switch Pi-hole subsystems to a different Github branch # Switch Pi-hole subsystems to a different Github branch.
# #
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
readonly PI_HOLE_FILES_DIR="/etc/.pihole" readonly PI_HOLE_FILES_DIR="/etc/.pihole"
PH_TEST="true" source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" PH_TEST="true"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# webInterfaceGitUrl set in basic-install.sh # webInterfaceGitUrl set in basic-install.sh
# webInterfaceDir set in basic-install.sh # webInterfaceDir set in basic-install.sh
@ -20,9 +21,100 @@ PH_TEST="true" source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
source "${setupVars}" source "${setupVars}"
update="false" update="false"
# Colour codes coltable="/opt/pihole/COL_TABLE"
red="\e[1;31m" source ${coltable}
def="\e[0m"
check_download_exists() {
status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1)
if grep -q "404" <<< "$status"; then
return 1
else
return 0
fi
}
FTLinstall() {
# Download and install FTL binary
local binary
binary="${1}"
local path
path="${2}"
local str
str="Installing FTL"
echo -ne " ${INFO} ${str}..."
if curl -sSL --fail "https://ftl.pi-hole.net/${path}" -o "/tmp/${binary}"; then
# Get sha1 of the binary we just downloaded for verification.
curl -sSL --fail "https://ftl.pi-hole.net/${path}.sha1" -o "/tmp/${binary}.sha1"
# Check if we just downloaded text, or a binary file.
cd /tmp || return 1
if sha1sum --status --quiet -c "${binary}".sha1; then
echo -n "transferred... "
stop_service pihole-FTL &> /dev/null
install -T -m 0755 "/tmp/${binary}" "/usr/bin/pihole-FTL"
rm "/tmp/${binary}" "/tmp/${binary}.sha1"
start_service pihole-FTL &> /dev/null
echo -e "${OVER} ${TICK} ${str}"
return 0
else
echo -e "${OVER} ${CROSS} ${str}"
echo -e " ${COL_LIGHT_RED}Error: Download of binary from ftl.pi-hole.net failed${COL_NC}"
return 1
fi
else
echo -e "${OVER} ${CROSS} ${str}"
echo -e " ${COL_LIGHT_RED}Error: URL not found${COL_NC}"
fi
}
get_binary_name() {
local machine
machine=$(uname -m)
local str
str="Detecting architecture"
echo -ne " ${INFO} ${str}..."
if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then
# ARM
local rev
rev=$(uname -m | sed "s/[^0-9]//g;")
local lib
lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }')
if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then
echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture"
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+)"
binary="pihole-FTL-arm-linux-gnueabihf"
else
echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv6 or lower) Using ARM binary"
binary="pihole-FTL-arm-linux-gnueabi"
fi
else
echo -e "${OVER} ${TICK} Detected ARM architecture"
binary="pihole-FTL-arm-linux-gnueabi"
fi
elif [[ "${machine}" == "ppc" ]]; then
# PowerPC
echo -e "${OVER} ${TICK} Detected PowerPC architecture"
binary="pihole-FTL-powerpc-linux-gnu"
elif [[ "${machine}" == "x86_64" ]]; then
# 64bit
echo -e "${OVER} ${TICK} Detected x86_64 architecture"
binary="pihole-FTL-linux-x86_64"
else
# Something else - we try to use 32bit executable and warn the user
if [[ ! "${machine}" == "i686" ]]; then
echo -e "${OVER} ${CROSS} ${str}...
${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable
Contact support if you experience issues (e.g: FTL not running)${COL_NC}"
else
echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture"
fi
binary="pihole-FTL-linux-x86_32"
fi
}
fully_fetch_repo() { fully_fetch_repo() {
# Add upstream branches to shallow clone # Add upstream branches to shallow clone
@ -40,61 +132,78 @@ fully_fetch_repo() {
get_available_branches() { get_available_branches() {
# Return available branches # Return available branches
local directory="${1}" local directory
directory="${1}"
local output
cd "${directory}" || return 1 cd "${directory}" || return 1
# Get reachable remote branches # Get reachable remote branches, but store STDERR as STDOUT variable
git remote show origin | grep 'tracked' | sed 's/tracked//;s/ //g' output=$( { git remote show origin | grep 'tracked' | sed 's/tracked//;s/ //g'; } 2>&1 )
echo "$output"
return return
} }
fetch_checkout_pull_branch() { fetch_checkout_pull_branch() {
# Check out specified branch # Check out specified branch
local directory="${1}" local directory
local branch="${2}" directory="${1}"
local branch
branch="${2}"
# Set the reference for the requested branch, fetch, check it put and pull it # Set the reference for the requested branch, fetch, check it put and pull it
cd "${directory}" cd "${directory}" || return 1
git remote set-branches origin "${branch}" || return 1 git remote set-branches origin "${branch}" || return 1
git stash --all --quiet &> /dev/null || true git stash --all --quiet &> /dev/null || true
git clean --force -d || true git clean --quiet --force -d || true
git fetch --quiet || return 1 git fetch --quiet || return 1
checkout_pull_branch "${directory}" "${branch}" || return 1 checkout_pull_branch "${directory}" "${branch}" || return 1
} }
checkout_pull_branch() { checkout_pull_branch() {
# Check out specified branch # Check out specified branch
local directory="${1}" local directory
local branch="${2}" directory="${1}"
local branch
branch="${2}"
local oldbranch local oldbranch
cd "${directory}" || return 1 cd "${directory}" || return 1
oldbranch="$(git symbolic-ref HEAD)" oldbranch="$(git symbolic-ref HEAD)"
git checkout "${branch}" || return 1 str="Switching to branch: '${branch}' from '${oldbranch}'"
echo -ne " ${INFO} $str"
git checkout "${branch}" --quiet || return 1
echo -e "${OVER} ${TICK} $str"
if [ "$(git diff "${oldbranch}" | grep -c "^")" -gt "0" ]; then
if [[ "$(git diff "${oldbranch}" | grep -c "^")" -gt "0" ]]; then
update="true" update="true"
fi fi
git pull || return 1 git_pull=$(git pull || return 1)
if [[ "$git_pull" == *"up-to-date"* ]]; then
echo -e " ${INFO} ${git_pull}"
else
echo -e "$git_pull\\n"
fi
return 0 return 0
} }
warning1() { warning1() {
echo " Please note that changing branches severely alters your Pi-hole subsystems" echo " Please note that changing branches severely alters your Pi-hole subsystems"
echo " Features that work on the master branch, may not on a development branch" echo " Features that work on the master branch, may not on a development branch"
echo -e " ${red}This feature is NOT supported unless a Pi-hole developer explicitly asks!${def}" echo -e " ${COL_LIGHT_RED}This feature is NOT supported unless a Pi-hole developer explicitly asks!${COL_NC}"
read -r -p " Have you read and understood this? [y/N] " response read -r -p " Have you read and understood this? [y/N] " response
case ${response} in case "${response}" in
[yY][eE][sS]|[yY]) [yY][eE][sS]|[yY])
echo "::: Continuing with branch change." echo ""
return 0 return 0
;; ;;
*) *)
echo "::: Branch change has been cancelled." echo -e "\\n ${INFO} Branch change has been cancelled"
return 1 return 1
;; ;;
esac esac
@ -109,22 +218,21 @@ checkout() {
# This is unlikely # This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo "::: Critical Error: Core Pi-hole repo is missing from system!" echo -e " ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
exit 1; exit 1;
fi fi
if [[ ${INSTALL_WEB} == "true" ]]; then if [[ "${INSTALL_WEB}" == "true" ]]; then
if ! is_repo "${webInterfaceDir}" ; then if ! is_repo "${webInterfaceDir}" ; then
echo "::: Critical Error: Web Admin repo is missing from system!" echo -e " ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
exit 1; exit 1;
fi fi
fi fi
if [[ -z "${1}" ]]; then if [[ -z "${1}" ]]; then
echo "::: No option detected. Please use 'pihole checkout <master|dev>'." echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}
echo "::: Or enter the repository and branch you would like to check out:" Try 'pihole checkout --help' for more information."
echo "::: 'pihole checkout <web|core> <branchname>'"
exit 1 exit 1
fi fi
@ -134,72 +242,117 @@ checkout() {
if [[ "${1}" == "dev" ]] ; then if [[ "${1}" == "dev" ]] ; then
# Shortcut to check out development branches # Shortcut to check out development branches
echo "::: Shortcut \"dev\" detected - checking out development / devel branches ..." echo -e " ${INFO} Shortcut \"dev\" detected - checking out development / devel branches..."
echo "::: Pi-hole core" echo ""
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo "Unable to pull Core developement branch"; exit 1; } echo -e " ${INFO} Pi-hole Core"
if [[ ${INSTALL_WEB} == "true" ]]; then fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core developement branch"; exit 1; }
echo "::: Web interface" if [[ "${INSTALL_WEB}" == "true" ]]; then
fetch_checkout_pull_branch "${webInterfaceDir}" "devel" || { echo "Unable to pull Web development branch"; exit 1; } echo ""
echo -e " ${INFO} Web interface"
fetch_checkout_pull_branch "${webInterfaceDir}" "devel" || { echo " ${CROSS} Unable to pull Web development branch"; exit 1; }
fi fi
echo "::: done!" #echo -e " ${TICK} Pi-hole Core"
get_binary_name
local path
path="development/${binary}"
FTLinstall "${binary}" "${path}"
elif [[ "${1}" == "master" ]] ; then elif [[ "${1}" == "master" ]] ; then
# Shortcut to check out master branches # Shortcut to check out master branches
echo "::: Shortcut \"master\" detected - checking out master branches ..." echo -e " ${INFO} Shortcut \"master\" detected - checking out master branches..."
echo "::: Pi-hole core" echo -e " ${INFO} Pi-hole core"
fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "master" || { echo "Unable to pull Core master branch"; exit 1; } fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "master" || { echo " ${CROSS} Unable to pull Core master branch"; exit 1; }
if [[ ${INSTALL_WEB} == "true" ]]; then if [[ ${INSTALL_WEB} == "true" ]]; then
echo "::: Web interface" echo -e " ${INFO} Web interface"
fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo "Unable to pull web master branch"; exit 1; } fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo " ${CROSS} Unable to pull Web master branch"; exit 1; }
fi fi
echo "::: done!" #echo -e " ${TICK} Web Interface"
get_binary_name
local path
path="master/${binary}"
FTLinstall "${binary}" "${path}"
elif [[ "${1}" == "core" ]] ; then elif [[ "${1}" == "core" ]] ; then
echo -n "::: Fetching remote branches for Pi-hole core from ${piholeGitUrl} ... " str="Fetching branches from ${piholeGitUrl}"
echo -ne " ${INFO} $str"
if ! fully_fetch_repo "${PI_HOLE_FILES_DIR}" ; then if ! fully_fetch_repo "${PI_HOLE_FILES_DIR}" ; then
echo "::: Fetching all branches for Pi-hole core repo failed!" echo -e "${OVER} ${CROSS} $str"
exit 1 exit 1
fi fi
corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}")) corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}"))
echo " done!"
echo "::: ${#corebranches[@]} branches available" if [[ "${corebranches[*]}" == *"master"* ]]; then
echo ":::" echo -e "${OVER} ${TICK} $str
# Have to user chosing the branch he wants ${INFO} ${#corebranches[@]} branches available for Pi-hole Core"
else
# Print STDERR output from get_available_branches
echo -e "${OVER} ${CROSS} $str\\n\\n${corebranches[*]}"
exit 1
fi
echo ""
# Have the user choose the branch they want
if ! (for e in "${corebranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then if ! (for e in "${corebranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then
echo "::: Requested branch \"${2}\" is not available!" echo -e " ${INFO} Requested branch \"${2}\" is not available"
echo "::: Available branches for core are:" echo -e " ${INFO} Available branches for Core are:"
for e in "${corebranches[@]}"; do echo "::: $e"; done for e in "${corebranches[@]}"; do echo " - $e"; done
exit 1 exit 1
fi fi
checkout_pull_branch "${PI_HOLE_FILES_DIR}" "${2}" checkout_pull_branch "${PI_HOLE_FILES_DIR}" "${2}"
elif [[ "${1}" == "web" && "${INSTALL_WEB}" == "true" ]] ; then elif [[ "${1}" == "web" ]] && [[ "${INSTALL_WEB}" == "true" ]] ; then
echo -n "::: Fetching remote branches for the web interface from ${webInterfaceGitUrl} ... " str="Fetching branches from ${webInterfaceGitUrl}"
echo -ne " ${INFO} $str"
if ! fully_fetch_repo "${webInterfaceDir}" ; then if ! fully_fetch_repo "${webInterfaceDir}" ; then
echo "::: Fetching all branches for Pi-hole web interface repo failed!" echo -e "${OVER} ${CROSS} $str"
exit 1 exit 1
fi fi
webbranches=($(get_available_branches "${webInterfaceDir}")) webbranches=($(get_available_branches "${webInterfaceDir}"))
echo " done!"
echo "::: ${#webbranches[@]} branches available" if [[ "${webbranches[*]}" == *"master"* ]]; then
echo ":::" echo -e "${OVER} ${TICK} $str
# Have to user chosing the branch he wants ${INFO} ${#webbranches[@]} branches available for Web Admin"
else
# Print STDERR output from get_available_branches
echo -e "${OVER} ${CROSS} $str\\n\\n${webbranches[*]}"
exit 1
fi
echo ""
# Have the user choose the branch they want
if ! (for e in "${webbranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then if ! (for e in "${webbranches[@]}"; do [[ "$e" == "${2}" ]] && exit 0; done); then
echo "::: Requested branch \"${2}\" is not available!" echo -e " ${INFO} Requested branch \"${2}\" is not available"
echo "::: Available branches for web are:" echo -e " ${INFO} Available branches for Web Admin are:"
for e in "${webbranches[@]}"; do echo "::: $e"; done for e in "${webbranches[@]}"; do echo " - $e"; done
exit 1 exit 1
fi fi
checkout_pull_branch "${webInterfaceDir}" "${2}" checkout_pull_branch "${webInterfaceDir}" "${2}"
elif [[ "${1}" == "ftl" ]] ; then
get_binary_name
local path
path="${2}/${binary}"
if check_download_exists "$path"; then
echo " ${TICK} Branch ${2} exists"
FTLinstall "${binary}" "${path}"
else else
echo "::: Requested option \"${1}\" is not available!" echo " ${CROSS} Requested branch \"${2}\" is not available"
ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') )
echo -e " ${INFO} Available branches for FTL are:"
for e in "${ftlbranches[@]}"; do echo " - $e"; done
exit 1
fi
else
echo -e " ${INFO} Requested option \"${1}\" is not available"
exit 1 exit 1
fi fi
# Force updating everything # Force updating everything
if [[ ! "${1}" == "web" && "${update}" == "true" ]]; then if [[ ( ! "${1}" == "web" && ! "${1}" == "ftl" ) && "${update}" == "true" ]]; then
echo "::: Running installer to upgrade your installation" echo -e " ${INFO} Running installer to upgrade your installation"
if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then
exit 0 exit 0
else else
echo "Unable to complete update, contact Pi-hole" echo -e " ${COL_LIGHT_RED} Error: Unable to complete update, please contact support${COL_NC}"
exit 1 exit 1
fi fi
fi fi

File diff suppressed because it is too large Load diff

View file

@ -8,8 +8,11 @@
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
colfile="/opt/pihole/COL_TABLE"
source ${colfile}
if [[ "$@" != *"quiet"* ]]; then if [[ "$@" != *"quiet"* ]]; then
echo -n "::: Flushing /var/log/pihole.log ..." echo -ne " ${INFO} Flushing /var/log/pihole.log ..."
fi fi
if [[ "$@" == *"once"* ]]; then if [[ "$@" == *"once"* ]]; then
# Nightly logrotation # Nightly logrotation
@ -41,5 +44,5 @@ else
fi fi
if [[ "$@" != *"quiet"* ]]; then if [[ "$@" != *"quiet"* ]]; then
echo "... done!" echo -e "${OVER} ${TICK} Flushed /var/log/pihole.log"
fi fi

View file

@ -10,10 +10,7 @@
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
# Variables # Variables
readonly ADMIN_INTERFACE_GIT_URL="https://github.com/pi-hole/AdminLTE.git" readonly ADMIN_INTERFACE_GIT_URL="https://github.com/pi-hole/AdminLTE.git"
readonly ADMIN_INTERFACE_DIR="/var/www/html/admin" readonly ADMIN_INTERFACE_DIR="/var/www/html/admin"
readonly PI_HOLE_GIT_URL="https://github.com/pi-hole/pi-hole.git" readonly PI_HOLE_GIT_URL="https://github.com/pi-hole/pi-hole.git"
@ -22,9 +19,10 @@ readonly PI_HOLE_FILES_DIR="/etc/.pihole"
# shellcheck disable=SC2034 # shellcheck disable=SC2034
PH_TEST=true PH_TEST=true
# Have to ignore the following rule as spaces in paths are not supported by ShellCheck
# shellcheck disable=SC1090 # shellcheck disable=SC1090
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# shellcheck disable=SC1091
source "/opt/pihole/COL_TABLE"
# is_repo() sourced from basic-install.sh # is_repo() sourced from basic-install.sh
# make_repo() sourced from basic-install.sh # make_repo() sourced from basic-install.sh
@ -52,15 +50,15 @@ GitCheckUpdateAvail() {
# defaults to the current one. # defaults to the current one.
REMOTE="$(git rev-parse "@{upstream}")" REMOTE="$(git rev-parse "@{upstream}")"
if [[ ${#LOCAL} == 0 ]]; then if [[ "${#LOCAL}" == 0 ]]; then
echo "::: Error: Local revision could not be obtained, ask Pi-hole support." echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support
echo "::: Additional debugging output:" Additional debugging output:${COL_NC}"
git status git status
exit exit
fi fi
if [[ ${#REMOTE} == 0 ]]; then if [[ "${#REMOTE}" == 0 ]]; then
echo "::: Error: Remote revision could not be obtained, ask Pi-hole support." echo -e "\\n ${COL_LIGHT_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support
echo "::: Additional debugging output:" Additional debugging output:${COL_NC}"
git status git status
exit exit
fi fi
@ -80,7 +78,6 @@ GitCheckUpdateAvail() {
} }
FTLcheckUpdate() { FTLcheckUpdate() {
local FTLversion local FTLversion
FTLversion=$(/usr/bin/pihole-FTL tag) FTLversion=$(/usr/bin/pihole-FTL tag)
local FTLlatesttag local FTLlatesttag
@ -96,57 +93,59 @@ FTLcheckUpdate() {
main() { main() {
local pihole_version_current local pihole_version_current
local web_version_current local web_version_current
local basicError="\\n ${COL_LIGHT_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}"
# shellcheck disable=1090,2154 # shellcheck disable=1090,2154
source "${setupVars}" source "${setupVars}"
# This is unlikely # This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo "::: Critical Error: Core Pi-hole repo is missing from system!" echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" Please re-run install script from https://pi-hole.net${COL_NC}"
exit 1; exit 1;
fi fi
echo "::: Checking for updates..." echo -e " ${INFO} Checking for updates..."
if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then
core_update=true core_update=true
echo "::: Pi-hole Core: update available" echo -e " ${INFO} Pi-hole Core:\\t${COL_YELLOW}update available${COL_NC}"
else else
core_update=false core_update=false
echo "::: Pi-hole Core: up to date" echo -e " ${INFO} Pi-hole Core:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
fi fi
if FTLcheckUpdate ; then if FTLcheckUpdate ; then
FTL_update=true FTL_update=true
echo "::: FTL: update available" echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}"
else else
FTL_update=false FTL_update=false
echo "::: FTL: up to date" echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
fi fi
# Logic: Don't update FTL when there is a core update available # Logic: Don't update FTL when there is a core update available
# since the core update will run the installer which will itself # since the core update will run the installer which will itself
# re-install (i.e. update) FTL # re-install (i.e. update) FTL
if ${FTL_update} && ! ${core_update}; then if ${FTL_update} && ! ${core_update}; then
echo ":::" echo ""
echo "::: FTL out of date" echo -e " ${INFO} FTL out of date"
FTLdetect FTLdetect
echo ":::" echo ""
fi fi
if [[ ${INSTALL_WEB} == true ]]; then if [[ "${INSTALL_WEB}" == true ]]; then
if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then
echo "::: Critical Error: Web Admin repo is missing from system!" echo -e "\\n ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!
echo "::: Please re-run install script from https://github.com/pi-hole/pi-hole" Please re-run install script from https://pi-hole.net${COL_NC}"
exit 1; exit 1;
fi fi
if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then
web_update=true web_update=true
echo "::: Web Interface: update available" echo -e " ${INFO} Web Interface:\\t${COL_YELLOW}update available${COL_NC}"
else else
web_update=false web_update=false
echo "::: Web Interface: up to date" echo -e " ${INFO} Web Interface:\\t${COL_LIGHT_GREEN}up to date${COL_NC}"
fi fi
# Logic # Logic
@ -161,72 +160,69 @@ main() {
if ! ${core_update} && ! ${web_update} ; then if ! ${core_update} && ! ${web_update} ; then
if ! ${FTL_update} ; then if ! ${FTL_update} ; then
echo ":::" echo ""
echo "::: Everything is up to date!" echo -e " ${TICK} Everything is up to date!"
exit 0 exit 0
fi fi
elif ! ${core_update} && ${web_update} ; then elif ! ${core_update} && ${web_update} ; then
echo ":::" echo ""
echo "::: Pi-hole Web Admin files out of date" echo -e " ${INFO} Pi-hole Web Admin files out of date"
getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}" getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}"
elif ${core_update} && ! ${web_update} ; then elif ${core_update} && ! ${web_update} ; then
echo ":::" echo ""
echo "::: Pi-hole core files out of date" echo -e " ${INFO} Pi-hole core files out of date"
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
echo -e "${basicError}" && exit 1
elif ${core_update} && ${web_update} ; then elif ${core_update} && ${web_update} ; then
echo ":::" echo ""
echo "::: Updating Pi-hole core and web admin files" echo -e " ${INFO} Updating Pi-hole core and web admin files"
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --unattended || \
echo -e "${basicError}" && exit 1
else else
echo "*** Update script has malfunctioned, fallthrough reached. Please contact support" echo -e " ${COL_LIGHT_RED}Update script has malfunctioned, please contact Pi-hole Support${COL_NC}"
exit 1 exit 1
fi fi
else # Web Admin not installed, so only verify if core is up to date else # Web Admin not installed, so only verify if core is up to date
if ! ${core_update}; then if ! ${core_update}; then
if ! ${FTL_update} ; then if ! ${FTL_update} ; then
echo ":::" echo ""
echo "::: Everything is up to date!" echo -e " ${INFO} Everything is up to date!"
exit 0 exit 0
fi fi
else else
echo ":::" echo ""
echo "::: Pi-hole core files out of date" echo -e " ${INFO} Pi-hole Core files out of date"
getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}"
${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo "Unable to complete update, contact Pi-hole" && exit 1 ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \
echo -e "${basicError}" && exit 1
fi fi
fi fi
if [[ "${web_update}" == true ]]; then if [[ "${web_update}" == true ]]; then
web_version_current="$(/usr/local/bin/pihole version --admin --current)" web_version_current="$(/usr/local/bin/pihole version --admin --current)"
echo ":::" echo ""
echo "::: Web Admin version is now at ${web_version_current/* v/v}}" echo -e " ${INFO} Web Admin version is now at ${web_version_current/* v/v}
echo "::: If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'" ${INFO} If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'"
fi fi
if [[ "${core_update}" == true ]]; then if [[ "${core_update}" == true ]]; then
pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)" pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)"
echo ":::" echo ""
echo "::: Pi-hole version is now at ${pihole_version_current/* v/v}}" echo -e " ${INFO} Pi-hole version is now at ${pihole_version_current/* v/v}
echo "::: If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'" ${INFO} If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'"
fi fi
if [[ ${FTL_update} == true ]]; then if [[ "${FTL_update}" == true ]]; then
FTL_version_current="$(/usr/local/bin/pihole version --ftl --current)" FTL_version_current="$(/usr/bin/pihole-FTL tag)"
echo ":::" echo -e "\\n ${INFO} FTL version is now at ${FTL_version_current/* v/v}"
echo "::: FTL version is now at ${FTL_version_current/* v/v}}"
start_service pihole-FTL start_service pihole-FTL
enable_service pihole-FTL enable_service pihole-FTL
fi fi
echo "" echo ""
exit 0 exit 0
} }
main main

59
advanced/Scripts/updatecheck.sh Executable file
View file

@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Checks for updates via GitHub
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
# Credit: https://stackoverflow.com/a/46324904
function json_extract() {
local key=$1
local json=$2
local string_regex='"([^"\]|\\.)*"'
local number_regex='-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]+)?'
local value_regex="${string_regex}|${number_regex}|true|false|null"
local pair_regex="\"${key}\"[[:space:]]*:[[:space:]]*(${value_regex})"
if [[ ${json} =~ ${pair_regex} ]]; then
echo $(sed 's/^"\|"$//g' <<< "${BASH_REMATCH[1]}")
else
return 1
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)")"
echo "${GITHUB_CORE_VERSION} ${GITHUB_WEB_VERSION} ${GITHUB_FTL_VERSION}" > "/etc/pihole/GitHubVersions"
function get_local_branch() {
# Return active branch
cd "${1}" 2> /dev/null || return 1
git rev-parse --abbrev-ref HEAD || return 1
}
CORE_BRANCH="$(get_local_branch /etc/.pihole)"
WEB_BRANCH="$(get_local_branch /var/www/html/admin)"
#FTL_BRANCH="$(pihole-FTL branch)"
# Don't store FTL branch until the next release of FTL which
# supports returning the branch in an easy way
FTL_BRANCH="XXX"
echo "${CORE_BRANCH} ${WEB_BRANCH} ${FTL_BRANCH}" > "/etc/pihole/localbranches"
function get_local_version() {
# Return active branch
cd "${1}" 2> /dev/null || return 1
git describe --long --dirty --tags || return 1
}
CORE_VERSION="$(get_local_version /etc/.pihole)"
WEB_VERSION="$(get_local_version /var/www/html/admin)"
FTL_VERSION="$(pihole-FTL version)"
echo "${CORE_VERSION} ${WEB_VERSION} ${FTL_VERSION}" > "/etc/pihole/localversions"

View file

@ -1,4 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# shellcheck disable=SC1090
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
@ -14,17 +16,23 @@ readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf"
# 03 -> wildcards # 03 -> wildcards
readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf"
coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then
source ${coltable}
fi
helpFunc() { helpFunc() {
echo "Usage: pihole -a [options] echo "Usage: pihole -a [options]
Example: pihole -a -p password Example: pihole -a -p password
Set options for the Admin Console Set options for the Admin Console
Options: Options:
-f, flush Flush the Pi-hole log
-p, password Set Admin Console password -p, password Set Admin Console password
-c, celsius Set Celsius as preferred temperature unit -c, celsius Set Celsius as preferred temperature unit
-f, fahrenheit Set Fahrenheit as preferred temperature unit -f, fahrenheit Set Fahrenheit as preferred temperature unit
-k, kelvin Set Kelvin as preferred temperature unit -k, kelvin Set Kelvin as preferred temperature unit
-r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address
-e, email Set an administrative contact address for the Block Page
-h, --help Show this help dialog -h, --help Show this help dialog
-i, interface Specify dnsmasq's interface listening behavior -i, interface Specify dnsmasq's interface listening behavior
Add '-h' for more info on interface usage" Add '-h' for more info on interface usage"
@ -58,6 +66,7 @@ delete_dnsmasq_setting() {
SetTemperatureUnit() { SetTemperatureUnit() {
change_setting "TEMPERATUREUNIT" "${unit}" change_setting "TEMPERATUREUNIT" "${unit}"
echo -e " ${TICK} Set temperature unit to ${unit}"
} }
HashPassword() { HashPassword() {
@ -84,12 +93,15 @@ SetWebPassword() {
readonly PASSWORD="${args[2]}" readonly PASSWORD="${args[2]}"
readonly CONFIRM="${PASSWORD}" readonly CONFIRM="${PASSWORD}"
else else
# 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 -p "Enter New Password (Blank for no password): " PASSWORD
echo "" echo ""
if [ "${PASSWORD}" == "" ]; then if [ "${PASSWORD}" == "" ]; then
change_setting "WEBPASSWORD" "" change_setting "WEBPASSWORD" ""
echo "Password Removed" echo -e " ${TICK} Password Removed"
exit 0 exit 0
fi fi
@ -98,12 +110,12 @@ SetWebPassword() {
fi fi
if [ "${PASSWORD}" == "${CONFIRM}" ] ; then if [ "${PASSWORD}" == "${CONFIRM}" ] ; then
hash=$(HashPassword ${PASSWORD}) hash=$(HashPassword "${PASSWORD}")
# Save hash to file # Save hash to file
change_setting "WEBPASSWORD" "${hash}" change_setting "WEBPASSWORD" "${hash}"
echo "New password set" echo -e " ${TICK} New password set"
else else
echo "Passwords don't match. Your password has not been changed" echo -e " ${CROSS} Passwords don't match. Your password has not been changed"
exit 1 exit 1
fi fi
} }
@ -208,16 +220,16 @@ SetExcludeClients() {
change_setting "API_EXCLUDE_CLIENTS" "${args[2]}" change_setting "API_EXCLUDE_CLIENTS" "${args[2]}"
} }
Poweroff(){
nohup bash -c "sleep 5; poweroff" &> /dev/null </dev/null &
}
Reboot() { Reboot() {
nohup bash -c "sleep 5; reboot" &> /dev/null </dev/null & nohup bash -c "sleep 5; reboot" &> /dev/null </dev/null &
} }
RestartDNS() { RestartDNS() {
if [ -x "$(command -v systemctl)" ]; then /usr/local/bin/pihole restartdns
systemctl restart dnsmasq &> /dev/null
else
service dnsmasq restart &> /dev/null
fi
} }
SetQueryLogOptions() { SetQueryLogOptions() {
@ -243,7 +255,12 @@ ProcessDHCPSettings() {
if [[ "${DHCP_LEASETIME}" == "0" ]]; then if [[ "${DHCP_LEASETIME}" == "0" ]]; then
leasetime="infinite" leasetime="infinite"
elif [[ "${DHCP_LEASETIME}" == "" ]]; then elif [[ "${DHCP_LEASETIME}" == "" ]]; then
leasetime="24h" leasetime="24"
change_setting "DHCP_LEASETIME" "${leasetime}"
elif [[ "${DHCP_LEASETIME}" == "24h" ]]; then
#Installation is affected by known bug, introduced in a previous version.
#This will automatically clean up setupVars.conf and remove the unnecessary "h"
leasetime="24"
change_setting "DHCP_LEASETIME" "${leasetime}" change_setting "DHCP_LEASETIME" "${leasetime}"
else else
leasetime="${DHCP_LEASETIME}h" leasetime="${DHCP_LEASETIME}h"
@ -275,8 +292,10 @@ ra-param=*,0,0
fi fi
else else
if [[ -f "${dhcpconfig}" ]]; then
rm "${dhcpconfig}" &> /dev/null rm "${dhcpconfig}" &> /dev/null
fi fi
fi
} }
EnableDHCP() { EnableDHCP() {
@ -373,12 +392,23 @@ RemoveDHCPStaticAddress() {
} }
SetHostRecord() { SetHostRecord() {
if [ -n "${args[3]}" ]; then if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
echo "Usage: pihole -a hostrecord <domain> [IPv4-address],[IPv6-address]
Example: 'pihole -a hostrecord home.domain.com 192.168.1.1,2001:db8:a0b:12f0::1'
Add a name to the DNS associated to an IPv4/IPv6 address
Options:
\"\" Empty: Remove host record
-h, --help Show this help dialog"
exit 0
fi
if [[ -n "${args[3]}" ]]; then
change_setting "HOSTRECORD" "${args[2]},${args[3]}" change_setting "HOSTRECORD" "${args[2]},${args[3]}"
echo "Setting host record for ${args[2]} -> ${args[3]}" echo -e " ${TICK} Setting host record for ${args[2]} to ${args[3]}"
else else
change_setting "HOSTRECORD" "" change_setting "HOSTRECORD" ""
echo "Removing host record" echo -e " ${TICK} Removing host record"
fi fi
ProcessDNSSettings ProcessDNSSettings
@ -387,6 +417,27 @@ SetHostRecord() {
RestartDNS RestartDNS
} }
SetAdminEmail() {
if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then
echo "Usage: pihole -a email <address>
Example: 'pihole -a email admin@address.com'
Set an administrative contact address for the Block Page
Options:
\"\" Empty: Remove admin contact
-h, --help Show this help dialog"
exit 0
fi
if [[ -n "${args[2]}" ]]; then
change_setting "ADMIN_EMAIL" "${args[2]}"
echo -e " ${TICK} Setting admin contact to ${args[2]}"
else
change_setting "ADMIN_EMAIL" ""
echo -e " ${TICK} Removing admin contact"
fi
}
SetListeningMode() { SetListeningMode() {
source "${setupVars}" source "${setupVars}"
@ -404,13 +455,13 @@ Interfaces:
fi fi
if [[ "${args[2]}" == "all" ]]; then if [[ "${args[2]}" == "all" ]]; then
echo "Listening on all interfaces, permiting all origins, hope you have a firewall!" echo -e " ${INFO} Listening on all interfaces, permiting all origins. Please use a firewall!"
change_setting "DNSMASQ_LISTENING" "all" change_setting "DNSMASQ_LISTENING" "all"
elif [[ "${args[2]}" == "local" ]]; then elif [[ "${args[2]}" == "local" ]]; then
echo "Listening on all interfaces, permitting only origins that are at most one hop away (local devices)" echo -e " ${INFO} Listening on all interfaces, permiting origins from one hop away (LAN)"
change_setting "DNSMASQ_LISTENING" "local" change_setting "DNSMASQ_LISTENING" "local"
else else
echo "Listening only on interface ${PIHOLE_INTERFACE}" echo -e " ${INFO} Listening only on interface ${PIHOLE_INTERFACE}"
change_setting "DNSMASQ_LISTENING" "single" change_setting "DNSMASQ_LISTENING" "single"
fi fi
@ -428,6 +479,11 @@ Teleporter() {
php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip" php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip"
} }
audit()
{
echo "${args[2]}" >> /etc/pihole/auditlog.list
}
main() { main() {
args=("$@") args=("$@")
@ -439,6 +495,7 @@ main() {
"setdns" ) SetDNSServers;; "setdns" ) SetDNSServers;;
"setexcludedomains" ) SetExcludeDomains;; "setexcludedomains" ) SetExcludeDomains;;
"setexcludeclients" ) SetExcludeClients;; "setexcludeclients" ) SetExcludeClients;;
"poweroff" ) Poweroff;;
"reboot" ) Reboot;; "reboot" ) Reboot;;
"restartdns" ) RestartDNS;; "restartdns" ) RestartDNS;;
"setquerylog" ) SetQueryLogOptions;; "setquerylog" ) SetQueryLogOptions;;
@ -450,10 +507,12 @@ main() {
"resolve" ) ResolutionSettings;; "resolve" ) ResolutionSettings;;
"addstaticdhcp" ) AddDHCPStaticAddress;; "addstaticdhcp" ) AddDHCPStaticAddress;;
"removestaticdhcp" ) RemoveDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;;
"hostrecord" ) SetHostRecord;; "-r" | "hostrecord" ) SetHostRecord "$3";;
"-e" | "email" ) SetAdminEmail "$3";;
"-i" | "interface" ) SetListeningMode "$@";; "-i" | "interface" ) SetListeningMode "$@";;
"-t" | "teleporter" ) Teleporter;; "-t" | "teleporter" ) Teleporter;;
"adlist" ) CustomizeAdLists;; "adlist" ) CustomizeAdLists;;
"audit" ) audit;;
* ) helpFunc;; * ) helpFunc;;
esac esac

View file

@ -1,136 +1,383 @@
/* CSS Reset */ /* Pi-hole: A black hole for Internet advertisements
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline; } * (c) 2017 Pi-hole, LLC (https://pi-hole.net)
article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { display: block; } * Network-wide ad blocking via your own hardware.
body { line-height: 1; } *
ol, ul { list-style: none; } * This file is copyright under the latest version of the EUPL.
blockquote, q { quotes: none; } * Please see LICENSE file for your rights under this license. */
blockquote:before, blockquote:after, q:before, q:after { content: ''; content: none; }
table { border-collapse: collapse; border-spacing: 0; }
html { height: 100%; overflow-x: hidden; }
/* General Style */ /* Text Customisation Options ======> */
a { color: rgba(0,60,120,0.95); text-decoration: none; } /* 1E3C5A */ .title:before { content: "Website Blocked"; }
a:hover { color: rgba(210,120,0,0.95); transition-duration: .2s; } /* 255, 128, 0 */ .altBtn:before { content: "Why am I here?"; }
divs a { border-bottom: 1px dashed rgba(30,60,90,0.3); } .linkPH:before { content: "About Pi-hole"; }
b { font-weight: bold; } .linkEmail:before { content: "Contact Admin"; }
i { font-style: italic; }
footer, pre, td { font-family: monospace; padding-left: 15px; } #bpOutput.add:before { content: "Info"; }
/*body, header { background: #E1E1E1; }*/ #bpOutput.add:after { content: "The domain is being whitelisted..."; }
#bpOutput.error:before, .unhandled:before { content: "Error"; }
#bpOutput.unhandled:after { content: "An unhandled exception occured. This may happen when your browser is unable to load jQuery, or when the webserver is denying access to the Pi-hole API."; }
#bpOutput.success:before { content: "Success"; }
#bpOutput.success:after { content: "Website has been whitelisted! You may need to flush your DNS cache"; }
.recentwl:before { content: "This site has been whitelisted. Please flush your DNS cache and/or restart your browser."; }
.unknown:before { content: "This website is not found in any of Pi-hole's blacklists. The reason you have arrived here is unknown."; }
.cname:before { content: "This site is an alias for "; } /* <a href="http://cname.com">cname.com</a> */
.cname:after { content: ", which may be blocked by Pi-hole."; }
.blacklist:before { content: "Manually Blacklisted"; }
.wildcard:before { content: "Manually Blacklisted by Wildcard"; }
.noblock:before { content: "Not found on any Blacklist"; }
#bpBlock:before { content: "Access to the following website has been denied:"; }
#bpFlag:before { content: "This is primarily due to being flagged as:"; }
#bpHelpTxt:before { content: "If you have an ongoing use for this website, please "; }
#bpHelpTxt a:before, #bpHelpTxt span:before { content: "ask the administrator"; }
#bpHelpTxt:after{ content: " of the Pi-hole on this network to have it whitelisted"; }
#bpBack:before { content: "Back to safety"; }
#bpInfo:before { content: "Technical Info"; }
#bpFoundIn:before { content: "This site is found in "; }
#bpFoundIn span:after { content: " of "; }
#bpFoundIn:after { content: " lists:"; }
#bpWhitelist:before { content: "Whitelist"; }
footer span:before { content: "Page generated on "; }
/* Hide whitelisting form entirely */
/* #bpWLButtons { display: none; } */
/* Text Customisation Options <=============================== */
/* http://necolas.github.io/normalize.css ======> */
html { font-family: sans-serif; line-height: 1.15; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%; }
body { margin: 0; }
article, aside, footer, header, nav, section { display: block; }
h1 { font-size: 2em; margin: 0.67em 0; }
figcaption, figure, main { display: block; }
figure { margin: 1em 40px; }
hr { box-sizing: content-box; height: 0; overflow: visible; }
pre { font-family: monospace, monospace; font-size: 1em; }
a { background-color: transparent; -webkit-text-decoration-skip: objects; }
a:active, a:hover { outline-width: 0; }
abbr[title] { border-bottom: none; text-decoration: underline; text-decoration: underline dotted; }
b, strong { font-weight: inherit; }
b, strong { font-weight: bolder; }
code, kbd, samp { font-family: monospace, monospace; font-size: 1em; }
dfn { font-style: italic; }
mark { background-color: #ff0; color: #000; }
small { font-size: 80%; }
sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline; }
sub { bottom: -0.25em; }
sup { top: -0.5em; }
audio, video { display: inline-block; }
audio:not([controls]) { display: none; height: 0; }
img { border-style: none; }
svg:not(:root) { overflow: hidden; }
button, input, optgroup, select, textarea { font-family: sans-serif; font-size: 100%; line-height: 1.15; margin: 0; }
button, input { overflow: visible; }
button, select { text-transform: none; }
button, html [type="button"], [type="reset"], [type="submit"] { -webkit-appearance: button; }
button::-moz-focus-inner, [type="button"]::-moz-focus-inner, [type="reset"]::-moz-focus-inner, [type="submit"]::-moz-focus-inner { border-style: none; padding: 0; }
button:-moz-focusring, [type="button"]:-moz-focusring, [type="reset"]:-moz-focusring, [type="submit"]:-moz-focusring { outline: 1px dotted ButtonText; }
fieldset { border: 1px solid #c0c0c0; margin: 0 2px; padding: 0.35em 0.625em 0.75em; }
legend { box-sizing: border-box; color: inherit; display: table; max-width: 100%; padding: 0; white-space: normal; }
progress { display: inline-block; vertical-align: baseline; }
textarea { overflow: auto; }
[type="checkbox"], [type="radio"] { box-sizing: border-box; padding: 0; }
[type="number"]::-webkit-inner-spin-button, [type="number"]::-webkit-outer-spin-button { height: auto; }
[type="search"] { -webkit-appearance: textfield; outline-offset: -2px; }
[type="search"]::-webkit-search-cancel-button, [type="search"]::-webkit-search-decoration { -webkit-appearance: none; }
::-webkit-file-upload-button { -webkit-appearance: button; font: inherit; }
details, menu { display: block; }
summary { display: list-item; }
canvas { display: inline-block; }
template { display: none; }
[hidden] { display: none; }
/* Normalize.css <=============================== */
html { font-size: 62.5%; }
a { color: #3c8dbc; text-decoration: none; }
a:hover { color: #72afda; text-decoration: underline; }
b { color: rgb(68,68,68); }
p { margin: 0; }
label, .buttons a {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
label, .buttons *:not([disabled]) { cursor: pointer; }
/* Touch device dark tap highlight */
header h1 a, label, .buttons * { -webkit-tap-highlight-color: transparent; }
/* Webkit Focus Glow */
textarea, input, button { outline: none; }
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 400;
src: local("Source Sans Pro"), local("SourceSansPro-Regular"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Regular.ttf") format("truetype");
}
@font-face {
font-family: "Source Sans Pro";
font-style: normal;
font-weight: 700;
src: local("Source Sans Pro Bold"), local("SourceSansPro-Bold"), url("/admin/style/vendor/SourceSansPro/SourceSansPro-Bold.ttf") format("truetype");
}
body { body {
background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); background: #dbdbdb url("/admin/img/boxed-bg.jpg") repeat fixed;
background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(190,190,190,0.95)); color: #333;
background-attachment: fixed; font: 1.4rem "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
color: rgba(64,64,64,0.95); line-height: 2.2rem;
font: 14px, sans-serif; }
line-height: 1em;
/* User is greeted with a splash page when browsing to Pi-hole IP address */
#splashpage { background: #222; color: rgba(255,255,255,0.7); text-align: center; }
#splashpage img { margin: 5px; width: 256px; }
#splashpage b { color: inherit; }
#bpWrapper {
margin: 0 auto;
max-width: 1250px;
box-shadow: 0 0 8px rgba(0,0,0,0.5);
} }
header { header {
min-width: 320px; background: #3c8dbc;
width: 100%;
text-shadow: 0 1px rgba(255,255,255,0.6);
display: table; display: table;
table-layout: fixed; position: relative;
border: 1px solid rgba(0,0,0,0.25);
border-top-color: rgba(255,255,255,0.85);
border-style: solid none;
background-image: -webkit-linear-gradient(top, rgba(240,240,240,0.95), rgba(220,220,220,0.95));
background-image: linear-gradient(to bottom, rgba(240,240,240,0.95), rgba(220,220,220,0.95));
box-shadow: 0 0 1px 1px rgba(0,0,0,0.04);
}
header h1, header div {
display: table-cell;
color: inherit;
font-weight: bold;
vertical-align: middle;
white-space: nowrap;
overflow: hidden;
box-sizing: border-box;
}
header h1 {
font-size: 22px;
font-weight: bold;
width: 100%; width: 100%;
padding: 8px 0;
text-indent: 32px;
background: url("http://pi.hole/admin/img/logo.svg") left no-repeat;
background-size: 30px 22px;
} }
header h1 a, h1 a:hover { color: inherit; } header h1, header h1 a, header .spc, header #bpAlt label {
header .alt { width: 85px; font-size: 0.8em; padding-right: 4px; text-align: right; line-height: 1.25em; } display: table-cell;
.active { color: green; } color: #fff;
.inactive { color: red; } white-space: nowrap;
vertical-align: middle;
height: 50px; /* Must match #bpAbout top value */
}
h1 a {
background-color: rgba(0,0,0,0.1);
font-family: "Helvetica Neue", Helvetica, Arial ,sans-serif;
font-size: 2rem;
font-weight: normal;
min-width: 230px;
text-align: center;
}
h1 a:hover, header #bpAlt:hover { background-color: rgba(0,0,0,0.12); color: inherit; text-decoration: none; }
header .spc { width: 100%; }
header #bpAlt label {
background: url("/admin/img/logo.svg") no-repeat center left 15px;
background-size: 15px 23px;
padding: 0 15px;
text-indent: 30px;
}
[type=checkbox][id$="Toggle"] { display: none; }
[type=checkbox][id$="Toggle"]:checked ~ #bpAbout,
[type=checkbox][id$="Toggle"]:checked ~ #bpMoreInfo {
display: block; }
/* Click anywhere else on screen to hide #bpAbout */
#bpAboutToggle:checked {
display: block;
height: 300px; /* VH Fallback */
height: 100vh;
left: 0;
top: 0;
opacity: 0;
position: absolute;
width: 100%;
}
#bpAbout {
background: #3c8dbc;
border-bottom-left-radius: 5px;
border: 1px solid #FFF;
border-right-width: 0;
box-shadow: -1px 1px 1px rgba(0,0,0,0.12);
box-sizing: border-box;
display: none;
font-size: 1.7rem;
top: 50px;
position: absolute;
right: 0;
width: 280px;
z-index: 1;
}
.aboutPH {
box-sizing: border-box;
color: rgba(255,255,255,0.8);
display: block;
padding: 10px;
width: 100%;
text-align: center;
}
.aboutImg {
background: url("/admin/img/logo.svg") no-repeat center;
background-size: 90px 90px;
height: 90px;
margin: 0 auto;
padding: 2px;
width: 90px;
}
.aboutPH p { margin: 10px 0; }
.aboutPH small { display: block; font-size: 1.2rem; }
.aboutLink {
background: #fff;
border-top: 1px solid #ddd;
display: table;
font-size: 1.4rem;
text-align: center;
width: 100%;
}
.aboutLink a {
display: table-cell;
padding: 14px;
min-width: 50%;
}
main { main {
display: block; background: #ecf0f5;
width: 80%; font-size: 1.65rem;
padding: 10px; padding: 10px;
font-size: 1em;
background-color: rgba(255,255,255,0.85);
margin: 8px auto;
box-sizing: border-box;
border: 1px solid rgba(0,0,0,0.25);
box-shadow: 4px 4px rgba(0,0,0,0.1);
line-height: 1.2em;
border-radius: 8px;
} }
h2 { /* Rgba is shared with .transparent th */ #bpOutput {
font: 1.15em sans-serif; background: #00c0ef;
background-color: rgba(255,0,0,0.4); border-radius: 3px;
text-shadow: none; border: 1px solid rgba(0,0,0,0.1);
line-height: 1.1em; color: #fff;
padding-bottom: 1px; font-size: 1.4rem;
margin-top: 8px; margin-bottom: 10px;
margin-bottom: 4px; margin-top: 5px;
background: -webkit-linear-gradient(left, rgba(0,0,0,0.25), transparent 80%) no-repeat; padding: 15px;
background: linear-gradient(to right, rgba(0,0,0,0.25), transparent 80%) no-repeat;
background-size: 100% 1px;
background-position: 0 17px;
} }
h2:first-child { margin-top: 0; } #bpOutput:before {
h2 ~ *:not(h2) { margin-left: 4px; } background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='7' height='14' viewBox='0 0 7 14'%3E%3Cpath fill='%23fff' d='M6,11a1.371,1.371,0,0,1,1,1v1a1.371,1.371,0,0,1-1,1H1a1.371,1.371,0,0,1-1-1V12a1.371,1.371,0,0,1,1-1H2V8H1A1.371,1.371,0,0,1,0,7V6A1.371,1.371,0,0,1,1,5H4A1.371,1.371,0,0,1,5,6v5H6ZM3.5,0A1.5,1.5,0,1,1,2,1.5,1.5,1.5,0,0,1,3.5,0Z'/%3E%3C/svg%3E") no-repeat center left;
li { padding: 2px 0; } display: block;
li::before { content: "\00BB\00a0"; } font-size: 1.8rem;
li a { position: relative; top: 1px; } /* Center bullet-point arrows */ text-indent: 15px;
}
/* Button Style */ #bpOutput.hidden { display: none; }
.buttons a, button, input, .transparent th a { /* Swapped rgba is shared with input[type='url'] */ #bpOutput.success { background: #00a65a; }
display: inline-block; #bpOutput.error { background: #dd4b39; }
color: rgba(32,32,32,0.9);
font-weight: bold; .blockMsg, .flagMsg {
font: bold 1.8rem Consolas, Courier, monospace;
padding: 5px 10px 10px 10px;
text-indent: 15px;
}
#bpHelpTxt { padding-bottom: 10px; }
.buttons {
border-spacing: 5px 0;
display: table;
width: 100%;
}
.buttons * {
-moz-appearance: none;
-webkit-appearance: none;
border-radius: 3px;
border: 1px solid rgba(0,0,0,0.1);
box-sizing: content-box;
display: table-cell;
font-size: 1.65rem;
margin-right: 5px;
min-height: 20px;
padding: 6px 12px;
position: relative;
text-align: center; text-align: center;
cursor: pointer; vertical-align: top;
text-shadow: 0 1px rgba(255,255,255,0.2); white-space: nowrap;
line-height: 0.86em; width: auto;
font-size: 1em;
padding: 4px 8px;
background: #FAFAFA;
background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.05), rgba(0,0,0,0.05));
background-image: linear-gradient(to bottom, rgba(255,255,255,0.05), rgba(0,0,0,0.05));
border: 1px solid rgba(0,0,0,0.25);
border-radius: 4px;
box-shadow: 0 1px 0 rgba(0,0,0,0.04);
} }
.buttons { white-space: nowrap; width: 100%; display: table; } .buttons a:hover { text-decoration: none; }
.buttons33 { white-space: nowrap; width: 33.333%; display: table; text-align: center; margin-left: 33.333% }
.mini a { width: 50%; }
a.safe { background-color: rgba(0,220,0,0.5); }
button.safe { background-color: rgba(0,220,0,0.5); }
a.warn { background-color: rgba(220,0,0,0.5); }
.blocked a, .mini a { display: table-cell; } /* Button hover dark overlay */
.blocked a.safe50 { width: 50%; background-color: rgba(0,220,0,0.5); } .buttons *:not(input):not([disabled]):hover {
.blocked a.safe33 { width: 33.333%; background-color: rgba(0,220,0,0.5); } background-image: linear-gradient(to bottom, rgba(0,0,0,0.1), rgba(0,0,0,0.1));
color: #FFF;
}
/* Types of text */ /* Button active shadow inset */
.msg { white-space: pre; overflow: auto; -webkit-overflow-scrolling: touch; display: block; line-height: 1.2em; font-weight: bold; font-size: 1.15em; margin: 4px 8px 8px 8px; white-space: pre-line; } .buttons *:not([disabled]):not(input):active {
box-shadow: inset 0 3px 5px rgba(0,0,0,0.125);
}
footer { font-size: 0.8em; text-align: center; width: 87%; margin: 4px auto; } /* Input border colour */
.buttons *:not([disabled]):hover, .buttons input:focus {
border-color: rgba(0,0,0,0.25);
}
#bpButtons * { width: 50%; color: #FFF; }
#bpBack { background-color: #00a65a; }
#bpInfo { background-color: #3c8dbc; }
#bpWhitelist { background-color: #dd4b39; }
#blockpage .buttons [type=password][disabled] { color: rgba(0,0,0,1); }
#blockpage .buttons [disabled] { color: rgba(0,0,0,0.55); background-color: #e3e3e3; }
#blockpage .buttons [type=password]:-ms-input-placeholder { color: rgba(51,51,51,0.8); }
input[type=password] { font-size: 1.5rem; }
@keyframes slidein { from { max-height: 0; opacity: 0; } to { max-height: 300px; opacity: 1; } }
#bpMoreToggle:checked ~ #bpMoreInfo { display: block; margin-top: 8px; animation: slidein 0.05s linear; }
#bpMoreInfo { display: none; margin-top: 10px; }
#bpQueryOutput {
font-size: 1.2rem;
line-height: 1.65rem;
margin: 5px 0 0 0;
overflow: auto;
padding: 0 5px;
-webkit-overflow-scrolling: touch;
}
#bpQueryOutput span { margin-right: 4px; }
#bpWLButtons { width: auto; margin-top: 10px; }
#bpWLButtons * { display: inline-block; }
#bpWLDomain { display: none; }
#bpWLPassword { width: 160px; }
#bpWhitelist { color: #fff; }
footer {
background: #fff;
border-top: 1px solid #d2d6de;
color: #444;
font: 1.2rem Consolas, Courier, monospace;
padding: 8px;
}
/* Responsive Content */
@media only screen and (max-width: 500px) {
h1 a { font-size: 1.8rem; min-width: 170px; }
footer span:before { content: "Generated "; }
footer span { display: block; }
}
@media only screen and (min-width: 1251px) {
#bpWrapper, footer { border-radius: 0 0 5px 5px; }
#bpAbout { border-right-width: 1px; }
}

View file

@ -1 +0,0 @@
var x = "Pi-hole: A black hole for Internet advertisements."

View file

@ -1,224 +1,345 @@
<?php <?php
/* Detailed Pi-hole Block Page: Show "Website Blocked" if user browses to site, but not to image/file requests based on the work of WaLLy3K for DietPi & Pi-Hole */ /* Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL.
* Please see LICENSE file for your rights under this license. */
function validIP($address){ // Sanitise HTTP_HOST output
if (preg_match('/[.:0]/', $address) && !preg_match('/[1-9a-f]/', $address)) { $serverName = htmlspecialchars($_SERVER["HTTP_HOST"]);
// Test if address contains either `:` or `0` but not 1-9 or a-f
return false;
}
return !filter_var($address, FILTER_VALIDATE_IP) === false;
}
$uri = escapeshellcmd($_SERVER['REQUEST_URI']); if (!is_file("/etc/pihole/setupVars.conf"))
$serverName = escapeshellcmd($_SERVER['SERVER_NAME']); die("[ERROR] File not found: <code>/etc/pihole/setupVars.conf</code>");
// If the server name is 'pi.hole', it's likely a user trying to get to the admin panel. // Get values from setupVars.conf
// Let's be nice and redirect them.
if ($serverName === 'pi.hole')
{
header('HTTP/1.1 301 Moved Permanently');
header("Location: /admin/");
}
// Retrieve server URI extension (EG: jpg, exe, php)
ini_set('pcre.recursion_limit',100);
$uriExt = pathinfo($uri, PATHINFO_EXTENSION);
// Define which URL extensions get rendered as "Website Blocked"
$webExt = array('asp', 'htm', 'html', 'php', 'rss', 'xml');
// Get IPv4 and IPv6 addresses from setupVars.conf (if available)
$setupVars = parse_ini_file("/etc/pihole/setupVars.conf"); $setupVars = parse_ini_file("/etc/pihole/setupVars.conf");
$ipv4 = isset($setupVars["IPV4_ADDRESS"]) ? explode("/", $setupVars["IPV4_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; $svPasswd = !empty($setupVars["WEBPASSWORD"]);
$ipv6 = isset($setupVars["IPV6_ADDRESS"]) ? explode("/", $setupVars["IPV6_ADDRESS"])[0] : $_SERVER['SERVER_ADDR']; $svEmail = (!empty($setupVars["ADMIN_EMAIL"]) && filter_var($setupVars["ADMIN_EMAIL"], FILTER_VALIDATE_EMAIL)) ? $setupVars["ADMIN_EMAIL"] : "";
unset($setupVars);
$AUTHORIZED_HOSTNAMES = array( // Set landing page location, found within /var/www/html/
$ipv4, $landPage = "../landing.php";
$ipv6,
str_replace(array("[","]"), array("",""), $_SERVER["SERVER_ADDR"]),
"pi.hole",
"localhost");
// Allow user set virtual hostnames
$virtual_host = getenv('VIRTUAL_HOST');
if (!empty($virtual_host))
array_push($AUTHORIZED_HOSTNAMES, $virtual_host);
// Immediately quit since we didn't block this page (the IP address or pi.hole is explicitly requested) // Define array for hostnames to be accepted as self address for splash page
if(validIP($serverName) || in_array($serverName,$AUTHORIZED_HOSTNAMES)) $authorizedHosts = [];
{ if (!empty($_SERVER["FQDN"])) {
http_response_code(404); // If setenv.add-environment = ("fqdn" => "true") is configured in lighttpd,
die(); // append $serverName to $authorizedHosts
array_push($authorizedHosts, $serverName);
} else if (!empty($_SERVER["VIRTUAL_HOST"])) {
// Append virtual hostname to $authorizedHosts
array_push($authorizedHosts, $_SERVER["VIRTUAL_HOST"]);
} }
if(in_array($uriExt, $webExt) || empty($uriExt)) // Set which extension types render as Block Page (Including "" for index.ext)
{ $validExtTypes = array("asp", "htm", "html", "php", "rss", "xml", "");
// Requested resource has an extension listed in $webExt
// or no extension (index access to some folder incl. the root dir) // Get extension of current URL
$showPage = true; $currentUrlExt = pathinfo($_SERVER["REQUEST_URI"], PATHINFO_EXTENSION);
}
else // Check if this is served over HTTP or HTTPS
{ if(isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on") {
// Something else $proto = "https";
$showPage = false; } else {
$proto = "http";
} }
// Handle incoming URI types // Set mobile friendly viewport
if (!$showPage) $viewPort = '<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>';
{
?> // Set response header
<html> function setHeader($type = "x") {
<head> header("X-Pi-hole: A black hole for Internet advertisements.");
<script>window.close();</script></head> if (isset($type) && $type === "js") header("Content-Type: application/javascript");
<body>
<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7">
</body>
</html>
<?php
die();
} }
// Get Pi-hole version // Determine block page type
$piHoleVersion = exec('cd /etc/.pihole/ && git describe --tags --abbrev=0'); if ($serverName === "pi.hole") {
// Redirect to Web Interface
exit(header("Location: /admin"));
} elseif (filter_var($serverName, FILTER_VALIDATE_IP) || in_array($serverName, $authorizedHosts)) {
// Set Splash Page output
$splashPage = "
<html><head>
$viewPort
<link rel='stylesheet' href='/pihole/blockingpage.css' type='text/css'/>
</head><body id='splashpage'><img src='/admin/img/logo.svg'/><br/>Pi-<b>hole</b>: Your black hole for Internet advertisements</body></html>
";
// Don't show the URI if it is the root directory // Set splash/landing page based off presence of $landPage
if($uri == "/") $renderPage = is_file(getcwd()."/$landPage") ? include $landPage : "$splashPage";
{
$uri = ""; // Unset variables so as to not be included in $landPage
unset($serverName, $svPasswd, $svEmail, $authorizedHosts, $validExtTypes, $currentUrlExt, $viewPort);
// Render splash/landing page when directly browsing via IP or authorised hostname
exit($renderPage);
} elseif ($currentUrlExt === "js") {
// Serve Pi-hole Javascript for blocked domains requesting JS
exit(setHeader("js").'var x = "Pi-hole: A black hole for Internet advertisements."');
} elseif (strpos($_SERVER["REQUEST_URI"], "?") !== FALSE && isset($_SERVER["HTTP_REFERER"])) {
// Serve blank image upon receiving REQUEST_URI w/ query string & HTTP_REFERRER
// e.g: An iframe of a blocked domain
exit(setHeader().'<html>
<head><script>window.close();</script></head>
<body><img src="data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACwAAAAAAQABAAACAkQBADs="></body>
</html>');
} elseif (!in_array($currentUrlExt, $validExtTypes) || substr_count($_SERVER["REQUEST_URI"], "?")) {
// Serve SVG upon receiving non $validExtTypes URL extension or query string
// e.g: Not an iframe of a blocked domain, such as when browsing to a file/query directly
// QoL addition: Allow the SVG to be clicked on in order to quickly show the full Block Page
$blockImg = '<a href="/"><svg xmlns="http://www.w3.org/2000/svg" width="110" height="16"><defs><style>a {text-decoration: none;} circle {stroke: rgba(152,2,2,0.5); fill: none; stroke-width: 2;} rect {fill: rgba(152,2,2,0.5);} text {opacity: 0.3; font: 11px Arial;}</style></defs><circle cx="8" cy="8" r="7"/><rect x="10.3" y="-6" width="2" height="12" transform="rotate(45)"/><text x="19.3" y="12">Blocked by Pi-hole</text></svg></a>';
exit(setHeader()."<html>
<head>$viewPort</head>
<body>$blockImg</body>
</html>");
} }
/* Start processing Block Page from here */
// Determine placeholder text based off $svPasswd presence
$wlPlaceHolder = empty($svPasswd) ? "No admin password set" : "Javascript disabled";
// Define admin email address text based off $svEmail presence
$bpAskAdmin = !empty($svEmail) ? '<a href="mailto:'.$svEmail.'?subject=Site Blocked: '.$serverName.'"></a>' : "<span/>";
// Determine if at least one block list has been generated
if (empty(glob("/etc/pihole/list.0.*.domains")))
die("[ERROR] There are no domain lists generated lists within <code>/etc/pihole/</code>! Please update gravity by running <code>pihole -g</code>, or repair Pi-hole using <code>pihole -r</code>.");
// Set location of adlists file
if (is_file("/etc/pihole/adlists.list")) {
$adLists = "/etc/pihole/adlists.list";
} elseif (is_file("/etc/pihole/adlists.default")) {
$adLists = "/etc/pihole/adlists.default";
} else {
die("[ERROR] File not found: <code>/etc/pihole/adlists.list</code>");
}
// Get all URLs starting with "http" or "www" from adlists and re-index array numerically
$adlistsUrls = array_values(preg_grep("/(^http)|(^www)/i", file($adLists, FILE_IGNORE_NEW_LINES)));
if (empty($adlistsUrls))
die("[ERROR]: There are no adlist URL's found within <code>$adLists</code>");
// Get total number of blocklists (Including Whitelist, Blacklist & Wildcard lists)
$adlistsCount = count($adlistsUrls) + 3;
// Set query timeout
ini_set("default_socket_timeout", 3);
// Logic for querying blocklists
function queryAds($serverName) {
// Determine the time it takes while querying adlists
$preQueryTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
$queryAds = file("http://127.0.0.1/admin/scripts/pi-hole/php/queryads.php?domain=$serverName&bp", FILE_IGNORE_NEW_LINES);
$queryAds = array_values(array_filter(preg_replace("/data:\s+/", "", $queryAds)));
$queryTime = sprintf("%.0f", (microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"]) - $preQueryTime);
// Exception Handling
try {
// Define Exceptions
if (strpos($queryAds[0], "No exact results") !== FALSE) {
// Return "none" into $queryAds array
return array("0" => "none");
} else if ($queryTime >= ini_get("default_socket_timeout")) {
// Connection Timeout
throw new Exception ("Connection timeout (".ini_get("default_socket_timeout")."s)");
} elseif (!strpos($queryAds[0], ".") !== false) {
// Unknown $queryAds output
throw new Exception ("Unhandled error message (<code>$queryAds[0]</code>)");
}
return $queryAds;
} catch (Exception $e) {
// Return exception as array
return array("0" => "error", "1" => $e->getMessage());
}
}
// Get results of queryads.php exact search
$queryAds = queryAds($serverName);
// Pass error through to Block Page
if ($queryAds[0] === "error")
die("[ERROR]: Unable to parse results from <i>queryads.php</i>: <code>".$queryAds[1]."</code>");
// Count total number of matching blocklists
$featuredTotal = count($queryAds);
// Place results into key => value array
$queryResults = null;
foreach ($queryAds as $str) {
$value = explode(" ", $str);
@$queryResults[$value[0]] .= "$value[1]";
}
// Determine if domain has been blacklisted, whitelisted, wildcarded or CNAME blocked
if (strpos($queryAds[0], "blacklist") !== FALSE) {
$notableFlagClass = "blacklist";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
} elseif (strpos($queryAds[0], "whitelist") !== FALSE) {
$notableFlagClass = "noblock";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
$wlInfo = "recentwl";
} elseif (strpos($queryAds[0], "wildcard") !== FALSE) {
$notableFlagClass = "wildcard";
$adlistsUrls = array("π" => substr($queryAds[0], 2));
} elseif ($queryAds[0] === "none") {
$featuredTotal = "0";
$notableFlagClass = "noblock";
// QoL addition: Determine appropriate info message if CNAME exists
// Suggests to the user that $serverName has a CNAME (alias) that may be blocked
$dnsRecord = dns_get_record("$serverName")[0];
if (array_key_exists("target", $dnsRecord)) {
$wlInfo = $dnsRecord['target'];
} else {
$wlInfo = "unknown";
}
}
// Set #bpOutput notification
$wlOutputClass = (isset($wlInfo) && $wlInfo === "recentwl") ? $wlInfo : "hidden";
$wlOutput = (isset($wlInfo) && $wlInfo !== "recentwl") ? "<a href='http://$wlInfo'>$wlInfo</a>" : "";
// Get Pi-hole Core version
$phVersion = exec("cd /etc/.pihole/ && git describe --long --tags");
// Print $execTime on development branches
// Testing for - is marginally faster than "git rev-parse --abbrev-ref HEAD"
if (explode("-", $phVersion)[1] != "0")
$execTime = microtime(true)-$_SERVER["REQUEST_TIME_FLOAT"];
// Please Note: Text is added via CSS to allow an admin to provide a localised
// language without the need to edit this file
?> ?>
<!DOCTYPE html> <!DOCTYPE html>
<!-- Pi-hole: A black hole for Internet advertisements
* (c) 2017 Pi-hole, LLC (https://pi-hole.net)
* Network-wide ad blocking via your own hardware.
*
* This file is copyright under the latest version of the EUPL. -->
<html> <html>
<head> <head>
<meta charset='UTF-8'/> <meta charset="UTF-8">
<title>Website Blocked</title> <?=$viewPort ?>
<link rel='stylesheet' href='http://pi.hole/pihole/blockingpage.css'/> <?=setHeader() ?>
<link rel='shortcut icon' href='http://pi.hole/admin/img/favicon.png' type='image/png'/> <meta name="robots" content="noindex,nofollow"/>
<meta name='viewport' content='width=device-width,initial-scale=1.0,maximum-scale=1.0, user-scalable=no'/> <meta http-equiv="x-dns-prefetch-control" content="off">
<meta name='robots' content='noindex,nofollow'/> <link rel="shortcut icon" href="<?=$proto ?>://pi.hole/admin/img/favicon.png" type="image/x-icon"/>
<link rel="stylesheet" href="<?=$proto ?>://pi.hole/pihole/blockingpage.css" type="text/css"/>
<title> <?=$serverName ?></title>
<script src="<?=$proto ?>://pi.hole/admin/scripts/vendor/jquery.min.js"></script>
<script>
window.onload = function () {
<?php
// Remove href fallback from "Back to safety" button
if ($featuredTotal > 0) echo '$("#bpBack").removeAttr("href");';
// Enable whitelisting if $svPasswd is present & JS is available
if (!empty($svPasswd) && $featuredTotal > 0) {
echo '$("#bpWLPassword, #bpWhitelist").prop("disabled", false);';
echo '$("#bpWLPassword").attr("placeholder", "Password");';
}
?>
}
</script>
</head> </head>
<body id="body"> <body id="blockpage"><div id="bpWrapper">
<header> <header>
<h1><a href='/'>Website Blocked</a></h1> <h1 id="bpTitle">
</header> <a class="title" href="/"><?php //Website Blocked ?></a>
<main> </h1>
<div>Access to the following site has been blocked:<br/> <div class="spc"></div>
<span class='pre msg'><?php echo $serverName.$uri; ?></span></div>
<div>If you have an ongoing use for this website, please ask the owner of the Pi-hole in your network to have it whitelisted.</div> <input id="bpAboutToggle" type="checkbox"/>
<input id="domain" type="hidden" value="<?php echo $serverName; ?>"> <div id="bpAbout">
<input id="quiet" type="hidden" value="yes"> <div class="aboutPH">
<button id="btnSearch" class="buttons blocked" type="button" style="visibility: hidden;"></button> <div class="aboutImg"/></div>
This page is blocked because it is explicitly contained within the following block list(s): <p>Open Source Ad Blocker
<pre id="output" style="width: 100%; height: 100%;" hidden="true"></pre><br/> <small>Designed for Raspberry Pi</small>
<div class='buttons blocked'> </p>
<a class='safe33' href='javascript:history.back()'>Go back</a>
<a class='safe33' id="whitelisting">Whitelist this page</a>
<a class='safe33' href='javascript:window.close()'>Close window</a>
</div> </div>
<div style="width: 98%; text-align: center; padding: 10px;" hidden="true" id="whitelistingform"> <div class="aboutLink">
<p>Note that whitelisting domains which are blocked using the wildcard method won't work.</p> <a class="linkPH" href="https://github.com/pi-hole/pi-hole/wiki/What-is-Pi-hole%3F-A-simple-explanation"><?php //About PH ?></a>
<p>Password required!</p><br/> <?php if (!empty($svEmail)) echo '<a class="linkEmail" href="mailto:'.$svEmail.'"></a>'; ?>
<form> </div>
<input name="list" type="hidden" value="white"><br/> </div>
Domain:<br/>
<input name="domain" value="<?php echo $serverName ?>" disabled><br/><br/> <div id="bpAlt">
Password:<br/> <label class="altBtn" for="bpAboutToggle"><?php //Why am I here? ?></label>
<input type="password" id="pw" name="pw"><br/><br/> </div>
<button class="buttons33 safe" id="btnAdd" type="button">Whitelist</button> </header>
</form><br/>
<pre id="whitelistingoutput" style="width: 100%; height: 100%; padding: 5px;" hidden="true"></pre><br/> <main>
<div id="bpOutput" class="<?=$wlOutputClass ?>"><?=$wlOutput ?></div>
<div id="bpBlock">
<p class="blockMsg"><?=$serverName ?></p>
</div>
<?php if(isset($notableFlagClass)) { ?>
<div id="bpFlag">
<p class="flagMsg <?=$notableFlagClass ?>"></p>
</div>
<?php } ?>
<div id="bpHelpTxt"><?=$bpAskAdmin ?></div>
<div id="bpButtons" class="buttons">
<a id="bpBack" onclick="javascript:history.back()" href="about:home"></a>
<?php if ($featuredTotal > 0) echo '<label id="bpInfo" for="bpMoreToggle"></label>'; ?>
</div>
<input id="bpMoreToggle" type="checkbox">
<div id="bpMoreInfo">
<span id="bpFoundIn"><span><?=$featuredTotal ?></span><?=$adlistsCount ?></span>
<pre id='bpQueryOutput'><?php if ($featuredTotal > 0) foreach ($queryResults as $num => $value) { echo "<span>[$num]:</span>$adlistsUrls[$num]\n"; } ?></pre>
<form id="bpWLButtons" class="buttons">
<input id="bpWLDomain" type="text" value="<?=$serverName ?>" disabled/>
<input id="bpWLPassword" type="password" placeholder="<?=$wlPlaceHolder ?>" disabled/><button id="bpWhitelist" type="button" disabled></button>
</form>
</div> </div>
</main> </main>
<footer>Generated <?php echo date('D g:i A, M d'); ?> by Pi-hole <?php echo $piHoleVersion; ?></footer>
<script src="http://pi.hole/admin/scripts/vendor/jquery.min.js"></script> <footer><span><?=date("l g:i A, F dS"); ?>.</span> Pi-hole <?=$phVersion ?> (<?=gethostname()."/".$_SERVER["SERVER_ADDR"]; if (isset($execTime)) printf("/%.2fs", $execTime); ?>)</footer>
</div>
<script> <script>
// Create event for when the output is appended to
(function($) {
var origAppend = $.fn.append;
$.fn.append = function () {
return origAppend.apply(this, arguments).trigger("append");
};
})(jQuery);
</script>
<script src="http://pi.hole/admin/scripts/pi-hole/js/queryads.js"></script>
<script>
function inIframe () {
try {
return window.self !== window.top;
} catch (e) {
return true;
}
}
// Try to detect if page is loaded within iframe
if(inIframe())
{
// Within iframe
// hide content of page
$('#body').hide();
// remove background
document.body.style.backgroundImage = "none";
}
else
{
// Query adlists
$( "#btnSearch" ).click();
}
$( "#whitelisting" ).on( "click", function(){ $( "#whitelistingform" ).removeAttr( "hidden" ); });
// Remove whitelist functionality if the domain was blocked because of a wildcard
$( "#output" ).bind("append", function(){
if($( "#output" ).contents()[0].data.indexOf("Wildcard blocking") !== -1)
{
$( "#whitelisting" ).hide();
$( "#whitelistingform" ).hide();
}
});
function add() { function add() {
var domain = $("#domain"); $("#bpOutput").removeClass("hidden error exception");
var pw = $("#pw"); $("#bpOutput").addClass("add");
if(domain.val().length === 0){ var domain = "<?=$serverName ?>";
var pw = $("#bpWLPassword");
if(domain.length === 0) {
return; return;
} }
$.ajax({ $.ajax({
url: "/admin/scripts/pi-hole/php/add.php", url: "/admin/scripts/pi-hole/php/add.php",
method: "post", method: "post",
data: {"domain":domain.val(), "list":"white", "pw":pw.val()}, data: {"domain":domain, "list":"white", "pw":pw.val()},
success: function(response) { success: function(response) {
$( "#whitelistingoutput" ).removeAttr( "hidden" ); if(response.indexOf("Pi-hole blocking") !== -1) {
if(response.indexOf("Pi-hole blocking") !== -1) setTimeout(function(){window.location.reload(1);}, 10000);
{ $("#bpOutput").removeClass("add");
// Reload page after 5 seconds $("#bpOutput").addClass("success");
setTimeout(function(){window.location.reload(1);}, 5000); } else {
$( "#whitelistingoutput" ).html("---> Success <---<br/>You may have to flush your DNS cache"); $("#bpOutput").removeClass("add");
$("#bpOutput").addClass("error");
$("#bpOutput").html(""+response+"");
} }
else
{
$( "#whitelistingoutput" ).html("---> "+response+" <---");
}
}, },
error: function(jqXHR, exception) { error: function(jqXHR, exception) {
$( "#whitelistingoutput" ).removeAttr( "hidden" ); $("#bpOutput").removeClass("add");
$( "#whitelistingoutput" ).html("---> Unknown Error <---"); $("#bpOutput").addClass("exception");
} }
}); });
} }
// Handle enter button for adding domains <?php if ($featuredTotal > 0) { ?>
$(document).keypress(function(e) { $(document).keypress(function(e) {
if(e.which === 13 && $("#pw").is(":focus")) { if(e.which === 13 && $("#bpWLPassword").is(":focus")) {
add(); add();
} }
}); });
$("#bpWhitelist").on("click", function() {
// Handle buttons
$("#btnAdd").on("click", function() {
add(); add();
}); });
<?php } ?>
</script> </script>
</body> </body></html>
</html>

View file

@ -2,18 +2,16 @@
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
# #
# lighttpd config for Pi-hole # Lighttpd config for Pi-hole
# #
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
############################################################################### ###############################################################################
# FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE. # # FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE # # ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
# # # #
# CHANGES SHOULD BE MADE IN A SEPERATE CONFIG FILE: # # CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE: #
# /etc/lighttpd/external.conf # # /etc/lighttpd/external.conf #
############################################################################### ###############################################################################
@ -39,9 +37,8 @@ server.port = 80
accesslog.filename = "/var/log/lighttpd/access.log" accesslog.filename = "/var/log/lighttpd/access.log"
accesslog.format = "%{%s}t|%V|%r|%s|%b" accesslog.format = "%{%s}t|%V|%r|%s|%b"
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" ) index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny = ( "~", ".inc" ) url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/" compress.cache-dir = "/var/cache/lighttpd/compress/"
@ -50,7 +47,10 @@ compress.filetype = ( "application/javascript", "text/css", "text/html
# default listening port for IPv6 falls back to the IPv4 port # default listening port for IPv6 falls back to the IPv4 port
include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port include_shell "/usr/share/lighttpd/use-ipv6.pl " + server.port
include_shell "/usr/share/lighttpd/create-mime.assign.pl" include_shell "/usr/share/lighttpd/create-mime.assign.pl"
include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
# Prevent Lighttpd from enabling Let's Encrypt SSL for every blocked domain
#include_shell "/usr/share/lighttpd/include-conf-enabled.pl"
include_shell "find /etc/lighttpd/conf-enabled -name '*.conf' -a ! -name 'letsencrypt.conf' -printf 'include \"%p\"\n' 2>/dev/null"
# If the URL starts with /admin, it is the Web interface # If the URL starts with /admin, it is the Web interface
$HTTP["url"] =~ "^/admin/" { $HTTP["url"] =~ "^/admin/" {
@ -59,22 +59,16 @@ $HTTP["url"] =~ "^/admin/" {
"X-Pi-hole" => "The Pi-hole Web interface is working!", "X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY" "X-Frame-Options" => "DENY"
) )
$HTTP["url"] =~ ".ttf$" {
# Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
} }
# Rewite js requests, must be out of $HTTP block due to bug #2526 # Block . files from being served, such as .git, .github, .gitignore
url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" ) $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("")
# If the URL does not start with /admin, then it is a query for an ad domain
$HTTP["url"] =~ "^(?!/admin)/.*" {
# Create a response header for debugging using curl -I
setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
}
# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/"
$HTTP["host"] == "pi.hole" {
$HTTP["url"] == "/" {
url.redirect = ( "" => "/admin/" )
}
} }
# Add user chosen options held in external file # Add user chosen options held in external file

View file

@ -7,13 +7,11 @@
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
############################################################################### ###############################################################################
# FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE. # # FILE AUTOMATICALLY OVERWRITTEN BY PI-HOLE INSTALL/UPDATE PROCEDURE. #
# ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE # # ANY CHANGES MADE TO THIS FILE AFTER INSTALL WILL BE LOST ON THE NEXT UPDATE #
# # # #
# CHANGES SHOULD BE MADE IN A SEPERATE CONFIG FILE: # # CHANGES SHOULD BE MADE IN A SEPARATE CONFIG FILE: #
# /etc/lighttpd/external.conf # # /etc/lighttpd/external.conf #
############################################################################### ###############################################################################
@ -42,7 +40,7 @@ accesslog.format = "%{%s}t|%V|%r|%s|%b"
index-file.names = ( "index.php", "index.html", "index.lighttpd.html" ) index-file.names = ( "index.php", "index.html", "index.lighttpd.html" )
url.access-deny = ( "~", ".inc" ) url.access-deny = ( "~", ".inc", ".md", ".yml", ".ini" )
static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" ) static-file.exclude-extensions = ( ".php", ".pl", ".fcgi" )
compress.cache-dir = "/var/cache/lighttpd/compress/" compress.cache-dir = "/var/cache/lighttpd/compress/"
@ -75,23 +73,20 @@ fastcgi.server = ( ".php" =>
# If the URL starts with /admin, it is the Web interface # If the URL starts with /admin, it is the Web interface
$HTTP["url"] =~ "^/admin/" { $HTTP["url"] =~ "^/admin/" {
# Create a response header for debugging using curl -I # Create a response header for debugging using curl -I
setenv.add-response-header = ( "X-Pi-hole" => "The Pi-hole Web interface is working!" ) setenv.add-response-header = (
"X-Pi-hole" => "The Pi-hole Web interface is working!",
"X-Frame-Options" => "DENY"
)
$HTTP["url"] =~ ".ttf$" {
# Allow Block Page access to local fonts
setenv.add-response-header = ( "Access-Control-Allow-Origin" => "*" )
}
} }
# Rewite js requests, must be out of $HTTP block due to bug #2526 # Block . files from being served, such as .git, .github, .gitignore
url.rewrite = ( "^(?!/admin/).*\.js$" => "pihole/index.js" ) $HTTP["url"] =~ "^/admin/\.(.*)" {
url.access-deny = ("")
# If the URL does not start with /admin, then it is a query for an ad domain
$HTTP["url"] =~ "^(?!/admin)/.*" {
# Create a response header for debugging using curl -I
setenv.add-response-header = ( "X-Pi-hole" => "A black hole for Internet advertisements." )
}
# Entering just "pi.hole" into a browser redirects to "pi.hole/admin/"
$HTTP["host"] == "pi.hole" {
$HTTP["url"] == "/" {
url.redirect = ( "" => "/admin/" )
}
} }
# Add user chosen options held in external file # Add user chosen options held in external file

View file

@ -14,8 +14,8 @@
# is updated or re-installed. Please make any changes to the appropriate crontab # is updated or re-installed. Please make any changes to the appropriate crontab
# or other cron file snippets. # or other cron file snippets.
# Pi-hole: Update the ad sources once a week on Sunday at 01:59 # Pi-hole: Update the ad sources once a week on Sunday at a random time in the
# Download any updates from the adlists # early morning. Download any updates from the adlists
59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity 59 1 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updateGravity
# Pi-hole: Update Pi-hole! Uncomment to enable auto update # Pi-hole: Update Pi-hole! Uncomment to enable auto update
@ -28,3 +28,6 @@
00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush once quiet 00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush once quiet
@reboot root /usr/sbin/logrotate /etc/pihole/logrotate @reboot root /usr/sbin/logrotate /etc/pihole/logrotate
# Pi-hole: Grab remote version and branch every 10 minutes
*/10 * * * * root PATH="$PATH:/usr/local/bin/" pihole updatechecker

File diff suppressed because it is too large Load diff

View file

@ -8,33 +8,57 @@
# This file is copyright under the latest version of the EUPL. # This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license. # Please see LICENSE file for your rights under this license.
source "/opt/pihole/COL_TABLE"
while true; do
read -rp " ${QST} Are you sure you would like to remove ${COL_WHITE}Pi-hole${COL_NC}? [y/N] " yn
case ${yn} in
[Yy]* ) break;;
[Nn]* ) echo -e "\n ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
* ) echo -e "\n ${COL_LIGHT_GREEN}Uninstall has been cancelled${COL_NC}"; exit 0;;
esac
done
# Must be root to uninstall # Must be root to uninstall
str="Root user check"
if [[ ${EUID} -eq 0 ]]; then if [[ ${EUID} -eq 0 ]]; then
echo "::: You are root." echo -e " ${TICK} ${str}"
else else
echo "::: Sudo will be used for the uninstall." # Check if sudo is actually installed
# Check if it is actually installed # If it isn't, exit because the uninstall can not complete
# If it isn't, exit because the unnstall cannot complete
if [ -x "$(command -v sudo)" ]; then if [ -x "$(command -v sudo)" ]; then
export SUDO="sudo" export SUDO="sudo"
else else
echo "::: Please install sudo or run this as root." echo -e " ${CROSS} ${str}
Script called with non-root privileges
The Pi-hole requires elevated privleges to uninstall"
exit 1 exit 1
fi fi
fi fi
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
PH_TEST="true"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# setupVars set in basic-install.sh
source "${setupVars}"
# distro_check() sourced from basic-install.sh
distro_check
# Install packages used by the Pi-hole
if [[ "${INSTALL_WEB}" == true ]]; then
# Install the Web dependencies
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}" "${PIHOLE_WEB_DEPS[@]}")
# Otherwise,
else
# just install the Core dependencies
DEPS=("${INSTALLER_DEPS[@]}" "${PIHOLE_DEPS[@]}")
fi
# Compatability # Compatability
if [ -x "$(command -v rpm)" ]; then if [ -x "$(command -v rpm)" ]; then
# Fedora Family # Fedora Family
if [ -x "$(command -v dnf)" ]; then
PKG_MANAGER="dnf"
else
PKG_MANAGER="yum"
fi
PKG_REMOVE="${PKG_MANAGER} remove -y" PKG_REMOVE="${PKG_MANAGER} remove -y"
PIHOLE_DEPS=( bind-utils bc dnsmasq lighttpd lighttpd-fastcgi php-common git curl unzip wget findutils )
package_check() { package_check() {
rpm -qa | grep ^$1- > /dev/null rpm -qa | grep ^$1- > /dev/null
} }
@ -43,9 +67,7 @@ if [ -x "$(command -v rpm)" ]; then
} }
elif [ -x "$(command -v apt-get)" ]; then elif [ -x "$(command -v apt-get)" ]; then
# Debian Family # Debian Family
PKG_MANAGER="apt-get"
PKG_REMOVE="${PKG_MANAGER} -y remove --purge" PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
PIHOLE_DEPS=( dnsutils bc dnsmasq lighttpd php5-common git curl unzip wget )
package_check() { package_check() {
dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -c "ok installed" dpkg-query -W -f='${Status}' "$1" 2>/dev/null | grep -c "ok installed"
} }
@ -54,62 +76,51 @@ elif [ -x "$(command -v apt-get)" ]; then
${SUDO} ${PKG_MANAGER} -y autoclean ${SUDO} ${PKG_MANAGER} -y autoclean
} }
else else
echo "OS distribution not supported" echo -e " ${CROSS} OS distribution not supported"
exit exit 1
fi fi
spinner() {
local pid=$1
local delay=0.50
local spinstr='/-\|'
while [ "$(ps a | awk '{print $1}' | grep "${pid}")" ]; do
local temp=${spinstr#?}
printf " [%c] " "${spinstr}"
local spinstr=${temp}${spinstr%"$temp}"}
sleep ${delay}
printf "\b\b\b\b\b\b"
done
printf " \b\b\b\b"
}
removeAndPurge() { removeAndPurge() {
# Purge dependencies # Purge dependencies
echo ":::" echo ""
for i in "${PIHOLE_DEPS[@]}"; do for i in "${DEPS[@]}"; do
package_check ${i} > /dev/null package_check ${i} > /dev/null
if [ $? -eq 0 ]; then if [[ "$?" -eq 0 ]]; then
while true; do while true; do
read -rp "::: Do you wish to remove ${i} from your system? [y/n]: " yn read -rp " ${QST} Do you wish to remove ${COL_WHITE}${i}${COL_NC} from your system? [Y/N] " yn
case ${yn} in case ${yn} in
[Yy]* ) printf ":::\tRemoving %s..." "${i}"; ${SUDO} ${PKG_REMOVE} "${i}" &> /dev/null & spinner $!; printf "done!\n"; break;; [Yy]* )
[Nn]* ) printf ":::\tSkipping %s\n" "${i}"; break;; echo -ne " ${INFO} Removing ${i}...";
* ) printf "::: You must answer yes or no!\n";; ${SUDO} ${PKG_REMOVE} "${i}" &> /dev/null;
echo -e "${OVER} ${INFO} Removed ${i}";
break;;
[Nn]* ) echo -e " ${INFO} Skipped ${i}"; break;;
esac esac
done done
else else
printf ":::\tPackage %s not installed... Not removing.\n" "${i}" echo -e " ${INFO} Package ${i} not installed"
fi fi
done done
# Remove dependency config files # Remove dnsmasq config files
echo "::: Removing dnsmasq config files..." ${SUDO} rm -f /etc/dnsmasq.conf /etc/dnsmasq.conf.orig /etc/dnsmasq.d/01-pihole.conf &> /dev/null
${SUDO} rm /etc/dnsmasq.conf /etc/dnsmasq.conf.orig /etc/dnsmasq.d/01-pihole.conf &> /dev/null echo -e " ${TICK} Removing dnsmasq config files"
# Take care of any additional package cleaning # Take care of any additional package cleaning
printf "::: Auto removing & cleaning remaining dependencies..." echo -ne " ${INFO} Removing & cleaning remaining dependencies..."
package_cleanup &> /dev/null & spinner $!; printf "done!\n"; package_cleanup &> /dev/null
echo -e "${OVER} ${TICK} Removed & cleaned up remaining dependencies"
# Call removeNoPurge to remove PiHole specific files # Call removeNoPurge to remove Pi-hole specific files
removeNoPurge removeNoPurge
} }
removeNoPurge() { removeNoPurge() {
echo ":::" # Only web directories/files that are created by Pi-hole should be removed
# Only web directories/files that are created by pihole should be removed. echo -ne " ${INFO} Removing Web Interface..."
echo "::: Removing the Pi-hole Web server files..."
${SUDO} rm -rf /var/www/html/admin &> /dev/null ${SUDO} rm -rf /var/www/html/admin &> /dev/null
${SUDO} rm -rf /var/www/html/pihole &> /dev/null ${SUDO} rm -rf /var/www/html/pihole &> /dev/null
${SUDO} rm /var/www/html/index.lighttpd.orig &> /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 folder can be removed.
if [ -d "/var/www/html" ]; then if [ -d "/var/www/html" ]; then
@ -117,65 +128,96 @@ removeNoPurge() {
${SUDO} rm -rf /var/www/html &> /dev/null ${SUDO} rm -rf /var/www/html &> /dev/null
fi fi
fi fi
echo -e "${OVER} ${TICK} Removed Web Interface"
# Attempt to preserve backwards compatibility with older versions # Attempt to preserve backwards compatibility with older versions
# to guarantee no additional changes were made to /etc/crontab after # to guarantee no additional changes were made to /etc/crontab after
# the installation of pihole, /etc/crontab.pihole should be permanently # the installation of pihole, /etc/crontab.pihole should be permanently
# preserved. # preserved.
if [[ -f /etc/crontab.orig ]]; then if [[ -f /etc/crontab.orig ]]; then
echo "::: Initial Pi-hole cron detected. Restoring the default system cron..."
${SUDO} mv /etc/crontab /etc/crontab.pihole ${SUDO} mv /etc/crontab /etc/crontab.pihole
${SUDO} mv /etc/crontab.orig /etc/crontab ${SUDO} mv /etc/crontab.orig /etc/crontab
${SUDO} service cron restart ${SUDO} service cron restart
echo -e " ${TICK} Restored the default system cron"
fi fi
# Attempt to preserve backwards compatibility with older versions # Attempt to preserve backwards compatibility with older versions
if [[ -f /etc/cron.d/pihole ]];then if [[ -f /etc/cron.d/pihole ]];then
echo "::: Removing cron.d/pihole..." ${SUDO} rm -f /etc/cron.d/pihole &> /dev/null
${SUDO} rm /etc/cron.d/pihole &> /dev/null echo -e " ${TICK} Removed /etc/cron.d/pihole"
fi fi
echo "::: Removing config files and scripts..."
package_check lighttpd > /dev/null package_check lighttpd > /dev/null
if [ $? -eq 1 ]; then if [[ $? -eq 1 ]]; then
${SUDO} rm -rf /etc/lighttpd/ &> /dev/null ${SUDO} rm -rf /etc/lighttpd/ &> /dev/null
echo -e " ${TICK} Removed lighttpd"
else else
if [ -f /etc/lighttpd/lighttpd.conf.orig ]; then if [ -f /etc/lighttpd/lighttpd.conf.orig ]; then
${SUDO} mv /etc/lighttpd/lighttpd.conf.orig /etc/lighttpd/lighttpd.conf ${SUDO} mv /etc/lighttpd/lighttpd.conf.orig /etc/lighttpd/lighttpd.conf
fi fi
fi fi
${SUDO} rm /etc/dnsmasq.d/adList.conf &> /dev/null ${SUDO} rm -f /etc/dnsmasq.d/adList.conf &> /dev/null
${SUDO} rm /etc/dnsmasq.d/01-pihole.conf &> /dev/null ${SUDO} rm -f /etc/dnsmasq.d/01-pihole.conf &> /dev/null
${SUDO} rm -rf /var/log/*pihole* &> /dev/null ${SUDO} rm -rf /var/log/*pihole* &> /dev/null
${SUDO} rm -rf /etc/pihole/ &> /dev/null ${SUDO} rm -rf /etc/pihole/ &> /dev/null
${SUDO} rm -rf /etc/.pihole/ &> /dev/null ${SUDO} rm -rf /etc/.pihole/ &> /dev/null
${SUDO} rm -rf /opt/pihole/ &> /dev/null ${SUDO} rm -rf /opt/pihole/ &> /dev/null
${SUDO} rm /usr/local/bin/pihole &> /dev/null ${SUDO} rm -f /usr/local/bin/pihole &> /dev/null
${SUDO} rm /etc/bash_completion.d/pihole &> /dev/null ${SUDO} rm -f /etc/bash_completion.d/pihole &> /dev/null
${SUDO} rm /etc/sudoers.d/pihole &> /dev/null ${SUDO} rm -f /etc/sudoers.d/pihole &> /dev/null
echo -e " ${TICK} Removed config files"
# If the pihole user exists, then remove # Remove FTL
if id "pihole" >/dev/null 2>&1; then if command -v pihole-FTL &> /dev/null; then
echo "::: Removing pihole user..." echo -ne " ${INFO} Removing pihole-FTL..."
${SUDO} userdel -r pihole
if [[ -x "$(command -v systemctl)" ]]; then
systemctl stop pihole-FTL
else
service pihole-FTL stop
fi fi
echo ":::" ${SUDO} rm -f /etc/init.d/pihole-FTL
printf "::: Finished removing PiHole from your system. Sorry to see you go!\n" ${SUDO} rm -f /usr/bin/pihole-FTL
printf "::: Reach out to us at https://github.com/pi-hole/pi-hole/issues if you need help\n" echo -e "${OVER} ${TICK} Removed pihole-FTL"
printf "::: Reinstall by simpling running\n:::\n:::\tcurl -sSL https://install.pi-hole.net | bash\n:::\n::: at any time!\n:::\n" fi
printf "::: PLEASE RESET YOUR DNS ON YOUR ROUTER/CLIENTS TO RESTORE INTERNET CONNECTIVITY!\n"
# If the pihole user exists, then remove
if id "pihole" &> /dev/null; then
${SUDO} userdel -r pihole 2> /dev/null
if [[ "$?" -eq 0 ]]; then
echo -e " ${TICK} Removed 'pihole' user"
else
echo -e " ${CROSS} Unable to remove 'pihole' user"
fi
fi
echo -e "\n We're sorry to see you go, but thanks for checking out Pi-hole!
If you need help, reach out to us on Github, Discourse, Reddit or Twitter
Reinstall at any time: ${COL_WHITE}curl -sSL https://install.pi-hole.net | bash${COL_NC}
${COL_LIGHT_RED}Please reset the DNS on your router/clients to restore internet connectivity
${COL_LIGHT_GREEN}Uninstallation Complete! ${COL_NC}"
} }
######### SCRIPT ########### ######### SCRIPT ###########
echo "::: Preparing to remove packages, be sure that each may be safely removed depending on your operating system." if command -v vcgencmd &> /dev/null; then
echo "::: (SAFE TO REMOVE ALL ON RASPBIAN)" echo -e " ${INFO} All dependencies are safe to remove on Raspbian"
else
echo -e " ${INFO} Be sure to confirm if any dependencies should not be removed"
fi
while true; do while true; do
read -rp "::: Do you wish to purge PiHole's dependencies from your OS? (You will be prompted for each package) [y/n]: " yn echo -e " ${INFO} ${COL_YELLOW}The following dependencies may have been added by the Pi-hole install:"
echo -n " "
for i in "${DEPS[@]}"; do
echo -n "${i} "
done
echo "${COL_NC}"
read -rp " ${QST} Do you wish to go through each dependency for removal? (Choosing No will leave all dependencies installed) [Y/n] " yn
case ${yn} in case ${yn} in
[Yy]* ) removeAndPurge; break;; [Yy]* ) removeAndPurge; break;;
[Nn]* ) removeNoPurge; break;; [Nn]* ) removeNoPurge; break;;
* ) removeAndPurge; break;;
esac esac
done done

File diff suppressed because it is too large Load diff

442
pihole
View file

@ -1,4 +1,5 @@
#!/bin/bash #!/bin/bash
# Pi-hole: A black hole for Internet advertisements # Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net) # (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware. # Network-wide ad blocking via your own hardware.
@ -10,6 +11,8 @@
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf"
readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
source "${colfile}"
# Must be root to use this tool # Must be root to use this tool
if [[ ! $EUID -eq 0 ]];then if [[ ! $EUID -eq 0 ]];then
@ -17,13 +20,13 @@ if [[ ! $EUID -eq 0 ]];then
exec sudo bash "$0" "$@" exec sudo bash "$0" "$@"
exit $? exit $?
else else
echo "::: sudo is needed to run pihole commands. Please run this script as root or install sudo." echo -e " ${CROSS} sudo is needed to run pihole commands. Please run this script as root or install sudo."
exit 1 exit 1
fi fi
fi fi
webpageFunc() { webpageFunc() {
source /opt/pihole/webpage.sh source "${PI_HOLE_SCRIPT_DIR}/webpage.sh"
main "$@" main "$@"
exit 0 exit 0
} }
@ -80,17 +83,28 @@ updateGravityFunc() {
exit 0 exit 0
} }
# Scan an array of files for matching strings
scanList(){ scanList(){
domain="${1}" # Escape full stops
list="${2}" local domain="${1//./\\.}" lists="${2}" type="${3:-}"
method="${3}"
if [[ ${method} == "-exact" ]] ; then # Prevent grep from printing file path
grep -i -E "(^|\s)${domain}($|\s)" "${list}" cd "/etc/pihole" || exit 1
else
grep -i "${domain}" "${list}" # Prevent grep -i matching slowly: http://bit.ly/2xFXtUX
fi export LC_CTYPE=C
# /dev/null forces filename to be printed when only one list has been generated
# shellcheck disable=SC2086
case "${type}" in
"exact" ) grep -i -E -l "(^|\\s)${domain}($|\\s|#)" ${lists} /dev/null;;
"wc" ) grep -i -o -m 1 "/${domain}/" ${lists};;
* ) grep -i "${domain}" ${lists} /dev/null;;
esac
} }
# Print each subdomain
# e.g: foo.bar.baz.com = "foo.bar.baz.com bar.baz.com baz.com com"
processWildcards() { processWildcards() {
IFS="." read -r -a array <<< "${1}" IFS="." read -r -a array <<< "${1}"
for (( i=${#array[@]}-1; i>=0; i-- )); do for (( i=${#array[@]}-1; i>=0; i-- )); do
@ -107,39 +121,192 @@ processWildcards() {
} }
queryFunc() { queryFunc() {
domain="${2}" shift
method="${3}" local options="$*" adlist="" all="" exact="" blockpage="" matchType="match"
lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt)
for list in ${lists[@]}; do if [[ "${options}" == "-h" ]] || [[ "${options}" == "--help" ]]; then
if [ -e "${list}" ]; then echo "Usage: pihole -q [option] <domain>
result=$(scanList ${domain} ${list} ${method}) Example: 'pihole -q -exact domain.com'
# Remove empty lines before couting number of results Query the adlists for a specified domain
count=$(sed '/^\s*$/d' <<< "$result" | wc -l)
echo "::: ${list} (${count} results)" Options:
if [[ ${count} > 0 ]]; then -adlist Print the name of the block list URL
echo "${result}" -exact Search the block lists for exact domain matches
-all Return all query matches within a block list
-h, --help Show this help dialog"
exit 0
fi fi
echo ""
if [[ ! -e "/etc/pihole/adlists.list" ]]; then
echo -e "${COL_LIGHT_RED}The file '/etc/pihole/adlists.list' was not found${COL_NC}"
exit 1
fi
# Handle valid options
if [[ "${options}" == *"-bp"* ]]; then
exact="exact"; blockpage=true
else else
echo "::: ${list} does not exist" [[ "${options}" == *"-adlist"* ]] && adlist=true
echo "" [[ "${options}" == *"-all"* ]] && all=true
if [[ "${options}" == *"-exact"* ]]; then
exact="exact"; matchType="exact ${matchType}"
fi
fi
# Strip valid options, leaving only the domain and invalid options
# This allows users to place the options before or after the domain
options=$(sed -E 's/ ?-(bp|adlists?|all|exact) ?//g' <<< "${options}")
# Handle remaining options
# If $options contain non ASCII characters, convert to punycode
case "${options}" in
"" ) str="No domain specified";;
*" "* ) str="Unknown query option specified";;
*[![:ascii:]]* ) domainQuery=$(idn2 "${options}");;
* ) domainQuery="${options}";;
esac
if [[ -n "${str:-}" ]]; then
echo -e "${str}${COL_NC}\\nTry 'pihole -q --help' for more information."
exit 1
fi
# Scan Whitelist and Blacklist
lists="whitelist.txt blacklist.txt"
mapfile -t results <<< "$(scanList "${domainQuery}" "${lists}" "${exact}")"
if [[ -n "${results[*]}" ]]; then
wbMatch=true
# Loop through each result in order to print unique file title once
for result in "${results[@]}"; do
fileName="${result%%.*}"
if [[ -n "${blockpage}" ]]; then
echo "π ${result}"
exit 0
elif [[ -n "${exact}" ]]; then
echo " ${matchType^} found in ${COL_BOLD}${fileName^}${COL_NC}"
else
# Only print filename title once per file
if [[ ! "${fileName}" == "${fileName_prev:-}" ]]; then
echo " ${matchType^} found in ${COL_BOLD}${fileName^}${COL_NC}"
fileName_prev="${fileName}"
fi
echo " ${result#*:}"
fi
done
fi
# Scan Wildcards
if [[ -e "${wildcardlist}" ]]; then
# Determine all subdomains, domain and TLDs
mapfile -t wildcards <<< "$(processWildcards "${domainQuery}")"
for match in "${wildcards[@]}"; do
# Search wildcard list for matches
mapfile -t results <<< "$(scanList "${match}" "${wildcardlist}" "wc")"
if [[ -n "${results[*]}" ]]; then
if [[ -z "${wcMatch:-}" ]] && [[ -z "${blockpage}" ]]; then
wcMatch=true
echo " ${matchType^} found in ${COL_BOLD}Wildcards${COL_NC}:"
fi
case "${blockpage}" in
true ) echo "π ${wildcardlist##*/}"; exit 0;;
* ) echo " *.${match}";;
esac
fi
done
fi
# Get version sorted *.domains filenames (without dir path)
lists=("$(cd "/etc/pihole" || exit 0; printf "%s\\n" -- *.domains | sort -V)")
# Query blocklists for occurences of domain
mapfile -t results <<< "$(scanList "${domainQuery}" "${lists[*]}" "${exact}")"
# Handle notices
if [[ -z "${wbMatch:-}" ]] && [[ -z "${wcMatch:-}" ]] && [[ -z "${results[*]}" ]]; then
echo -e " ${INFO} No ${exact/t/t }results found for ${COL_BOLD}${domainQuery}${COL_NC} found within block lists"
exit 0
elif [[ -z "${results[*]}" ]]; then
# Result found in WL/BL/Wildcards
exit 0
elif [[ -z "${all}" ]] && [[ "${#results[*]}" -ge 100 ]]; then
echo -e " ${INFO} Over 100 ${exact/t/t }results found for ${COL_BOLD}${domainQuery}${COL_NC}
This can be overridden using the -all option"
exit 0
fi
# Remove unwanted content from non-exact $results
if [[ -z "${exact}" ]]; then
# Delete lines starting with #
# Remove comments after domain
# Remove hosts format IP address
mapfile -t results <<< "$(IFS=$'\n'; sed \
-e "/:#/d" \
-e "s/[ \\t]#.*//g" \
-e "s/:.*[ \\t]/:/g" \
<<< "${results[*]}")"
# Exit if result was in a comment
[[ -z "${results[*]}" ]] && exit 0
fi
# Get adlist file content as array
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
for adlistUrl in $(< "/etc/pihole/adlists.list"); do
if [[ "${adlistUrl:0:4}" =~ (http|www.) ]]; then
adlists+=("${adlistUrl}")
fi
done
fi
# Print "Exact matches for" title
if [[ -n "${exact}" ]] && [[ -z "${blockpage}" ]]; then
plural=""; [[ "${#results[*]}" -gt 1 ]] && plural="es"
echo " ${matchType^}${plural} for ${COL_BOLD}${domainQuery}${COL_NC} found in:"
fi
for result in "${results[@]}"; do
fileName="${result/:*/}"
# Determine *.domains URL using filename's number
if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then
fileNum="${fileName/list./}"; fileNum="${fileNum%%.*}"
fileName="${adlists[$fileNum]}"
# Discrepency occurs when adlists has been modified, but Gravity has not been run
if [[ -z "${fileName}" ]]; then
fileName="${COL_LIGHT_RED}(no associated adlists URL found)${COL_NC}"
fi
fi
if [[ -n "${blockpage}" ]]; then
echo "${fileNum} ${fileName}"
elif [[ -n "${exact}" ]]; then
echo " ${fileName}"
else
if [[ ! "${fileName}" == "${fileName_prev:-}" ]]; then
count=""
echo " ${matchType^} found in ${COL_BOLD}${fileName}${COL_NC}:"
fileName_prev="${fileName}"
fi
: $((count++))
# Print matching domain if $max_count has not been reached
[[ -z "${all}" ]] && max_count="50"
if [[ -z "${all}" ]] && [[ "${count}" -ge "${max_count}" ]]; then
[[ "${count}" -gt "${max_count}" ]] && continue
echo " ${COL_GRAY}Over ${count} results found, skipping rest of file${COL_NC}"
else
echo " ${result#*:}"
fi
fi fi
done done
# Scan for possible wildcard matches
if [ -e "${wildcardlist}" ]; then
local wildcards=($(processWildcards "${domain}"))
for domain in ${wildcards[@]}; do
result=$(scanList "\/${domain}\/" ${wildcardlist})
# Remove empty lines before couting number of results
count=$(sed '/^\s*$/d' <<< "$result" | wc -l)
if [[ ${count} > 0 ]]; then
echo "::: Wildcard blocking ${domain} (${count} results)"
echo "${result}"
echo ""
fi
done
fi
exit 0 exit 0
} }
@ -162,22 +329,39 @@ versionFunc() {
} }
restartDNS() { restartDNS() {
dnsmasqPid=$(pidof dnsmasq) local svcOption svc str output status
if [[ "${dnsmasqPid}" ]]; then svcOption="${1:-}"
# Service already running - reload config
if [[ -x "$(command -v systemctl)" ]]; then # Determine if we should reload or restart dnsmasq
systemctl restart dnsmasq if [[ "${svcOption}" =~ "reload" ]]; then
# Using SIGHUP will NOT re-read any *.conf files
svc="killall -s SIGHUP dnsmasq"
else else
service dnsmasq restart # Get PID of dnsmasq to determine if it needs to start or restart
fi if pidof dnsmasq &> /dev/null; then
svcOption="restart"
else else
# Service not running, start it up svcOption="start"
if [[ -x "$(command -v systemctl)" ]]; then fi
systemctl start dnsmasq svc="service dnsmasq ${svcOption}"
fi
# Print output to Terminal, but not to Web Admin
str="${svcOption^}ing DNS service"
[[ -t 1 ]] && echo -ne " ${INFO} ${str}..."
output=$( { ${svc}; } 2>&1 )
status="$?"
if [[ "${status}" -eq 0 ]]; then
[[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}"
else else
service dnsmasq start [[ ! -t 1 ]] && local OVER=""
fi echo -e "${OVER} ${CROSS} ${output}"
fi fi
# Send signal to FTL to have it re-parse the gravity files
killall -s SIGHUP pihole-FTL
} }
piholeEnable() { piholeEnable() {
@ -190,6 +374,7 @@ Time:
#s Disable Pi-hole functionality for # second(s) #s Disable Pi-hole functionality for # second(s)
#m Disable Pi-hole functionality for # minute(s)" #m Disable Pi-hole functionality for # minute(s)"
exit 0 exit 0
elif [[ "${1}" == "0" ]]; then elif [[ "${1}" == "0" ]]; then
# Disable Pi-hole # 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\/gravity.list/#addn-hosts=\/etc\/pihole\/gravity.list/' /etc/dnsmasq.d/01-pihole.conf
@ -197,34 +382,57 @@ Time:
if [[ -e "$wildcardlist" ]]; then if [[ -e "$wildcardlist" ]]; then
mv "$wildcardlist" "/etc/pihole/wildcard.list" mv "$wildcardlist" "/etc/pihole/wildcard.list"
fi fi
echo "::: Blocking has been disabled!"
if [[ $# > 1 ]]; then if [[ $# > 1 ]]; then
if [[ "${2}" == *"s"* ]]; then local error=false
if [[ "${2}" == *"s" ]]; then
tt=${2%"s"} tt=${2%"s"}
echo "::: Blocking will be re-enabled in ${tt} seconds" if [[ "${tt}" =~ ^-?[0-9]+$ ]];then
local str="Disabling blocking for ${tt} seconds"
echo -e " ${INFO} ${str}..."
local str="Blocking will be re-enabled in ${tt} seconds"
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null & nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
elif [[ "${2}" == *"m"* ]]; then else
local error=true
fi
elif [[ "${2}" == *"m" ]]; then
tt=${2%"m"} tt=${2%"m"}
echo "::: Blocking will be re-enabled in ${tt} minutes" if [[ "${tt}" =~ ^-?[0-9]+$ ]];then
local str="Disabling blocking for ${tt} minutes"
echo -e " ${INFO} ${str}..."
local str="Blocking will be re-enabled in ${tt} minutes"
tt=$((${tt}*60)) tt=$((${tt}*60))
nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null & nohup bash -c "sleep ${tt}; pihole enable" </dev/null &>/dev/null &
else else
echo "::: Unknown format for delayed reactivation of the blocking!" local error=true
echo "::: Example:"
echo "::: pihole disable 5s - will disable blocking for 5 seconds"
echo "::: pihole disable 7m - will disable blocking for 7 minutes"
echo "::: Blocking will not automatically be re-enabled!"
fi fi
elif [[ -n "${2}" ]]; then
local error=true
else
echo -e " ${INFO} Disabling blocking"
fi
if [[ ${error} == true ]];then
echo -e " ${COL_LIGHT_RED}Unknown format for delayed reactivation of the blocking!${COL_NC}"
echo -e " Try 'pihole disable --help' for more information."
exit 1
fi
local str="Pi-hole Disabled"
fi fi
else else
# Enable Pi-hole # Enable Pi-hole
echo "::: Blocking has been enabled!" echo -e " ${INFO} Enabling blocking"
local str="Pi-hole Enabled"
sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf
if [[ -e "/etc/pihole/wildcard.list" ]]; then if [[ -e "/etc/pihole/wildcard.list" ]]; then
mv "/etc/pihole/wildcard.list" "$wildcardlist" mv "/etc/pihole/wildcard.list" "$wildcardlist"
fi fi
fi fi
restartDNS restartDNS
echo -e "${OVER} ${TICK} ${str}"
} }
piholeLogging() { piholeLogging() {
@ -243,54 +451,60 @@ Options:
sed -i 's/^log-queries/#log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^log-queries/#log-queries/' /etc/dnsmasq.d/01-pihole.conf
sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf
pihole -f pihole -f
echo "::: Logging has been disabled!" echo -e " ${INFO} Disabling logging..."
local str="Logging has been disabled!"
elif [[ "${1}" == "on" ]]; then elif [[ "${1}" == "on" ]]; then
# Enable logging # Enable logging
sed -i 's/^#log-queries/log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^#log-queries/log-queries/' /etc/dnsmasq.d/01-pihole.conf
sed -i 's/^QUERY_LOGGING=false/QUERY_LOGGING=true/' /etc/pihole/setupVars.conf sed -i 's/^QUERY_LOGGING=false/QUERY_LOGGING=true/' /etc/pihole/setupVars.conf
echo "::: Logging has been enabled!" echo -e " ${INFO} Enabling logging..."
local str="Logging has been enabled!"
else else
echo "::: Invalid option passed, please pass 'on' or 'off'" echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}
Try 'pihole logging --help' for more information."
exit 1 exit 1
fi fi
restartDNS restartDNS
echo -e "${OVER} ${TICK} ${str}"
} }
piholeStatus() { statusFunc() {
if [[ "$(netstat -plnt | grep -c ':53 ')" -gt "0" ]]; then local addnConfigs
# Determine if service is running on port 53 (Cr: https://superuser.com/a/806331)
if (echo > /dev/tcp/localhost/53) >/dev/null 2>&1; then
if [[ "${1}" != "web" ]]; then if [[ "${1}" != "web" ]]; then
echo "::: DNS service is running" echo -e " ${TICK} DNS service is running"
fi fi
else else
if [[ "${1}" == "web" ]]; then case "${1}" in
echo "-1"; "web") echo "-1";;
else *) echo -e " ${CROSS} DNS service is NOT running";;
echo "::: DNS service is NOT running" esac
fi return 0
return
fi fi
if [[ "$(grep -i "^#addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then # Determine if Pi-hole's addn-hosts configs are commented out
# List is commented out addnConfigs=$(grep -i "addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)
if [[ "${1}" == "web" ]]; then
echo 0; if [[ "${addnConfigs}" =~ "#" ]]; 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
# Configs are set
case "${1}" in
"web") echo 1;;
*) echo -e " ${TICK} Pi-hole blocking is Enabled";;
esac
else else
echo "::: Pi-hole blocking is Disabled"; # No configs were found
fi case "${1}" in
elif [[ "$(grep -i "^addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then "web") echo 99;;
# List set *) echo -e " ${INFO} No hosts file linked to dnsmasq, adding it in enabled state";;
if [[ "${1}" == "web" ]]; then esac
echo 1;
else
echo "::: Pi-hole blocking is Enabled";
fi
else
# Addn-host not found
if [[ "${1}" == "web" ]]; then
echo 99
else
echo "::: No hosts file linked to dnsmasq, adding it in enabled state"
fi
# Add addn-host= to dnsmasq # Add addn-host= to dnsmasq
echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf
restartDNS restartDNS
@ -298,8 +512,20 @@ piholeStatus() {
} }
tailFunc() { tailFunc() {
echo "Press Ctrl-C to exit" echo -e " ${INFO} Press Ctrl-C to exit"
tail -F /var/log/pihole.log
# Retrieve IPv4/6 addresses
source /etc/pihole/setupVars.conf
# Strip date from each line
# Colour blocklist/blacklist/wildcard entries as red
# Colour A/AAAA/DHCP strings as white
# 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,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
-e "s,.*,${COL_GRAY}&${COL_NC},"
exit 0 exit 0
} }
@ -312,6 +538,7 @@ Switch Pi-hole subsystems to a different Github branch
Repositories: Repositories:
core [branch] Change the branch of Pi-hole's core subsystem core [branch] Change the branch of Pi-hole's core subsystem
web [branch] Change the branch of Admin Console subsystem web [branch] Change the branch of Admin Console subsystem
ftl [branch] Change the branch of Pi-hole's FTL subsystem
Branches: Branches:
master Update subsystems to the latest stable release master Update subsystems to the latest stable release
@ -326,12 +553,12 @@ Branches:
tricorderFunc() { tricorderFunc() {
if [[ ! -p "/dev/stdin" ]]; then if [[ ! -p "/dev/stdin" ]]; then
echo "Please do not call Tricorder directly." echo -e " ${INFO} Please do not call Tricorder directly"
exit 1 exit 1
fi fi
if ! timeout 2 nc -z tricorder.pi-hole.net 9998 &> /dev/null; then if ! (echo > /dev/tcp/tricorder.pi-hole.net/9998) >/dev/null 2>&1; then
echo "Unable to connect to Pi-hole's Tricorder server." echo -e " ${CROSS} Unable to connect to Pi-hole's Tricorder server"
exit 1 exit 1
fi fi
@ -339,9 +566,10 @@ tricorderFunc() {
openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin
exit "$?" exit "$?"
else else
echo "Your debug log will be transmitted unencrypted via plain-text" echo -e " ${INFO} ${COL_YELLOW}Security Notice${COL_NC}: ${COL_WHITE}openssl${COL_NC} is not installed
echo "There is a possibility that this could be intercepted by a third party" Your debug log will be transmitted unencrypted via plain-text
echo "If you wish to cancel, press Ctrl-C to exit within 10 seconds" There is a possibility that this could be intercepted by a third party
If you wish to cancel, press Ctrl-C to exit within 10 seconds"
secs="10" secs="10"
while [[ "$secs" -gt "0" ]]; do while [[ "$secs" -gt "0" ]]; do
echo -ne "." echo -ne "."
@ -354,6 +582,11 @@ tricorderFunc() {
fi fi
} }
updateCheckFunc() {
"${PI_HOLE_SCRIPT_DIR}"/updatecheck.sh "$@"
exit 0
}
helpFunc() { helpFunc() {
echo "Usage: pihole [options] echo "Usage: pihole [options]
Example: 'pihole -w -h' Example: 'pihole -w -h'
@ -382,7 +615,7 @@ Options:
-l, logging Specify whether the Pi-hole log should be used -l, logging Specify whether the Pi-hole log should be used
Add '-h' for more info on logging usage Add '-h' for more info on logging usage
-q, query Query the adlists for a specified domain -q, query Query the adlists for a specified domain
Add '-exact' AFTER a specified domain for exact match Add '-h' for more info on query usage
-up, updatePihole Update Pi-hole subsystems -up, updatePihole Update Pi-hole subsystems
-v, version Show installed versions of Pi-hole, Admin Console & FTL -v, version Show installed versions of Pi-hole, Admin Console & FTL
Add '-h' for more info on version usage Add '-h' for more info on version usage
@ -419,11 +652,12 @@ case "${1}" in
"uninstall" ) uninstallFunc;; "uninstall" ) uninstallFunc;;
"enable" ) piholeEnable 1;; "enable" ) piholeEnable 1;;
"disable" ) piholeEnable 0 "$2";; "disable" ) piholeEnable 0 "$2";;
"status" ) piholeStatus "$2";; "status" ) statusFunc "$2";;
"restartdns" ) restartDNS;; "restartdns" ) restartDNS "$2";;
"-a" | "admin" ) webpageFunc "$@";; "-a" | "admin" ) webpageFunc "$@";;
"-t" | "tail" ) tailFunc;; "-t" | "tail" ) tailFunc;;
"checkout" ) piholeCheckoutFunc "$@";; "checkout" ) piholeCheckoutFunc "$@";;
"tricorder" ) tricorderFunc;; "tricorder" ) tricorderFunc;;
"updatechecker" ) updateCheckFunc;;
* ) helpFunc;; * ) helpFunc;;
esac esac

View file

@ -9,6 +9,10 @@ SETUPVARS = {
'PIHOLE_DNS_2' : '4.2.2.2' 'PIHOLE_DNS_2' : '4.2.2.2'
} }
tick_box="[\x1b[1;32m\xe2\x9c\x93\x1b[0m]".decode("utf-8")
cross_box="[\x1b[1;31m\xe2\x9c\x97\x1b[0m]".decode("utf-8")
info_box="[i]".decode("utf-8")
def test_setupVars_are_sourced_to_global_scope(Pihole): def test_setupVars_are_sourced_to_global_scope(Pihole):
''' currently update_dialogs sources setupVars with a dot, ''' currently update_dialogs sources setupVars with a dot,
then various other functions use the variables. then various other functions use the variables.
@ -55,6 +59,8 @@ def test_setupVars_saved_to_file(Pihole):
TERM=xterm TERM=xterm
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
{} {}
mkdir -p /etc/dnsmasq.d
version_check_dnsmasq
finalExports finalExports
cat /etc/pihole/setupVars.conf cat /etc/pihole/setupVars.conf
'''.format(set_setup_vars)) '''.format(set_setup_vars))
@ -74,7 +80,7 @@ def test_configureFirewall_firewalld_running_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
configureFirewall configureFirewall
''') ''')
expected_stdout = 'Configuring FirewallD for httpd and dnsmasq.' expected_stdout = 'Configuring FirewallD for httpd and dnsmasq'
assert expected_stdout in configureFirewall.stdout assert expected_stdout in configureFirewall.stdout
firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout
assert 'firewall-cmd --state' in firewall_calls assert 'firewall-cmd --state' in firewall_calls
@ -89,7 +95,7 @@ def test_configureFirewall_firewalld_disabled_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
configureFirewall configureFirewall
''') ''')
expected_stdout = 'No active firewall detected.. skipping firewall configuration.' expected_stdout = 'No active firewall detected.. skipping firewall configuration'
assert expected_stdout in configureFirewall.stdout assert expected_stdout in configureFirewall.stdout
def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole): def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole):
@ -173,105 +179,13 @@ def test_installPiholeWeb_fresh_install_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
installPiholeWeb installPiholeWeb
''') ''')
assert 'Installing pihole custom index page...' in installWeb.stdout assert info_box + ' Installing blocking page...' in installWeb.stdout
assert tick_box + ' Creating directory for blocking page, and copying files' in installWeb.stdout
assert cross_box + ' Backing up index.lighttpd.html' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' in installWeb.stdout assert 'No default index.lighttpd.html file found... not backing up' in installWeb.stdout
assert tick_box + ' Installing sudoer file' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_empty_directory_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed in an emtpy directory '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'index.php missing, replacing...' in installWeb.stdout
assert 'index.js missing, replacing...' in installWeb.stdout
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_index_php_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/index.php
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'Existing index.php detected, not overwriting' in installWeb.stdout
assert 'index.js missing, replacing...' in installWeb.stdout
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_index_js_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/index.js
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'index.php missing, replacing...' in installWeb.stdout
assert 'Existing index.js detected, not overwriting' in installWeb.stdout
assert 'blockingpage.css missing, replacing...' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_blockingpage_css_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/blockingpage.css
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'index.php missing, replacing...' in installWeb.stdout
assert 'index.js missing, replacing...' in installWeb.stdout
assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory
def test_installPiholeWeb_already_populated_no_errors(Pihole):
''' confirms all web page assets from Core repo are installed when necessary '''
installWeb = Pihole.run('''
source /opt/pihole/basic-install.sh
mkdir -p /var/www/html/pihole
touch /var/www/html/pihole/index.php
touch /var/www/html/pihole/index.js
touch /var/www/html/pihole/blockingpage.css
installPiholeWeb
''')
assert 'Installing pihole custom index page...' in installWeb.stdout
assert 'No default index.lighttpd.html file found... not backing up' not in installWeb.stdout
assert 'Existing index.php detected, not overwriting' in installWeb.stdout
assert 'index.php missing, replacing...' not in installWeb.stdout
assert 'Existing index.js detected, not overwriting' in installWeb.stdout
assert 'index.js missing, replacing...' not in installWeb.stdout
assert 'Existing blockingpage.css detected, not overwriting' in installWeb.stdout
assert 'blockingpage.css missing, replacing... ' not in installWeb.stdout
web_directory = Pihole.run('ls -r /var/www/html/pihole').stdout
assert 'index.php' in web_directory
assert 'index.js' in web_directory
assert 'blockingpage.css' in web_directory assert 'blockingpage.css' in web_directory
def test_update_package_cache_success_no_errors(Pihole): def test_update_package_cache_success_no_errors(Pihole):
@ -281,9 +195,8 @@ def test_update_package_cache_success_no_errors(Pihole):
distro_check distro_check
update_package_cache update_package_cache
''') ''')
assert 'Updating local cache of available packages...' in updateCache.stdout assert tick_box + ' Update local cache of available packages' in updateCache.stdout
assert 'ERROR' not in updateCache.stdout assert 'Error: Unable to update package cache.' not in updateCache.stdout
assert 'done!' in updateCache.stdout
def test_update_package_cache_failure_no_errors(Pihole): def test_update_package_cache_failure_no_errors(Pihole):
''' confirms package cache was not updated''' ''' confirms package cache was not updated'''
@ -293,9 +206,8 @@ def test_update_package_cache_failure_no_errors(Pihole):
distro_check distro_check
update_package_cache update_package_cache
''') ''')
assert 'Updating local cache of available packages...' in updateCache.stdout assert cross_box + ' Update local cache of available packages' in updateCache.stdout
assert 'ERROR' in updateCache.stdout assert 'Error: Unable to update package cache.' in updateCache.stdout
assert 'done!' not in updateCache.stdout
def test_FTL_detect_aarch64_no_errors(Pihole): def test_FTL_detect_aarch64_no_errors(Pihole):
''' confirms only aarch64 package is downloaded for FTL engine ''' ''' confirms only aarch64 package is downloaded for FTL engine '''
@ -307,7 +219,11 @@ def test_FTL_detect_aarch64_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
FTLdetect FTLdetect
''') ''')
expected_stdout = 'Detected ARM-aarch64 architecture' expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected ARM-aarch64 architecture'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_armv6l_no_errors(Pihole): def test_FTL_detect_armv6l_no_errors(Pihole):
@ -320,7 +236,11 @@ def test_FTL_detect_armv6l_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
FTLdetect FTLdetect
''') ''')
expected_stdout = 'Detected ARM-hf architecture (armv6 or lower)' expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected ARM-hf architecture (armv6 or lower)'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_armv7l_no_errors(Pihole): def test_FTL_detect_armv7l_no_errors(Pihole):
@ -333,7 +253,11 @@ def test_FTL_detect_armv7l_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
FTLdetect FTLdetect
''') ''')
expected_stdout = 'Detected ARM-hf architecture (armv7+)' expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected ARM-hf architecture (armv7+)'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_x86_64_no_errors(Pihole): def test_FTL_detect_x86_64_no_errors(Pihole):
@ -342,7 +266,11 @@ def test_FTL_detect_x86_64_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
FTLdetect FTLdetect
''') ''')
expected_stdout = 'Detected x86_64 architecture' expected_stdout = info_box + ' FTL Checks...'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Detected x86_64 architecture'
assert expected_stdout in detectPlatform.stdout
expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
def test_FTL_detect_unknown_no_errors(Pihole): def test_FTL_detect_unknown_no_errors(Pihole):
@ -363,9 +291,12 @@ def test_FTL_download_aarch64_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
FTLinstall pihole-FTL-aarch64-linux-gnu FTLinstall pihole-FTL-aarch64-linux-gnu
''') ''')
expected_stdout = 'done' expected_stdout = tick_box + ' Downloading and Installing FTL'
assert expected_stdout in download_binary.stdout assert expected_stdout in download_binary.stdout
assert 'failed' not in download_binary.stdout error = 'Error: Download of binary from Github failed'
assert error not in download_binary.stdout
error = 'Error: URL not found'
assert error not in download_binary.stdout
def test_FTL_download_unknown_fails_no_errors(Pihole): def test_FTL_download_unknown_fails_no_errors(Pihole):
''' confirms unknown binary is not downloaded for FTL engine ''' ''' confirms unknown binary is not downloaded for FTL engine '''
@ -374,9 +305,10 @@ def test_FTL_download_unknown_fails_no_errors(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
FTLinstall pihole-FTL-mips FTLinstall pihole-FTL-mips
''') ''')
expected_stdout = 'failed' expected_stdout = cross_box + ' Downloading and Installing FTL'
assert expected_stdout in download_binary.stdout assert expected_stdout in download_binary.stdout
assert 'done' not in download_binary.stdout error = 'Error: URL not found'
assert error in download_binary.stdout
def test_FTL_binary_installed_and_responsive_no_errors(Pihole): def test_FTL_binary_installed_and_responsive_no_errors(Pihole):
''' confirms FTL binary is copied and functional in installed location ''' ''' confirms FTL binary is copied and functional in installed location '''
@ -410,7 +342,7 @@ def test_IPv6_only_link_local(Pihole):
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
useIPv6dialog useIPv6dialog
''') ''')
expected_stdout = 'Found neither IPv6 ULA nor GUA address, blocking IPv6 ads will not be enabled' expected_stdout = 'Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled'
assert expected_stdout in detectPlatform.stdout assert expected_stdout in detectPlatform.stdout
def test_IPv6_only_ULA(Pihole): def test_IPv6_only_ULA(Pihole):