Compare commits

...

159 Commits

Author SHA1 Message Date
RD WebDesign
ac78066657 Fix the github suggestion mess
Some checks failed
CodeQL / Analyze (pull_request) Has been cancelled
Test Supported Distributions / smoke-tests (pull_request) Has been cancelled
Test Supported Distributions / distro-test (alpine_3_21) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (alpine_3_22) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (centos_10) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (centos_9) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (debian_11) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (debian_12) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (debian_13) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (fedora_40) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (fedora_41) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (fedora_42) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (ubuntu_20) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (ubuntu_22) (pull_request) Has been cancelled
Test Supported Distributions / distro-test (ubuntu_24) (pull_request) Has been cancelled
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-10-19 16:14:55 -03:00
RD WebDesign
f4a395cb06 Apply suggestion from @MichaIng
Do not handle HTTP code "000" separately.
Use curl error messages for every error, including unknown/unexpected HTTP codes or non-HTTP errors.

Co-authored-by: MichaIng <micha@dietpi.com>
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-10-19 14:48:54 -03:00
RD WebDesign
0cfc02cbab Apply suggestion from @MichaIng
Remove the STDERR mute from curl command.

The `-s` flag already mutes all curl errors, so STDERR would only contain something if curl itself (or a shared library) is damaged/missing.

Co-authored-by: MichaIng <micha@dietpi.com>
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-10-19 14:41:39 -03:00
RD WebDesign
4e191da1a0 Improve curl error message including exit code and error message
This commit replaces the 3 digits http_code returned by curl with the json
output. This output contains all returned values, including http_code,
exitcode and errormsg.

Using json format, the old http_error "000" string is formated as a number "0".

Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-10-17 22:37:45 -03:00
yubiuser
d0ce95c385 Tiny change to make prev2 also a local variable (#6420) 2025-10-15 05:54:57 +02:00
casperklein
9e8e360591 add missing local variable
Signed-off-by: casperklein <casperklein@users.noreply.github.com>
2025-10-15 04:37:19 +02:00
yubiuser
5b53661a79 Bump tox from 4.30.3 to 4.31.0 in /test in the python-dependencies group (#6418) 2025-10-11 12:13:53 +02:00
yubiuser
a626e7de48 Bump the github-actions-dependencies group with 2 updates (#6417) 2025-10-11 12:12:52 +02:00
dependabot[bot]
71dbf2715a Bump tox from 4.30.3 to 4.31.0 in /test in the python-dependencies group
Bumps the python-dependencies group in /test with 1 update: [tox](https://github.com/tox-dev/tox).


Updates `tox` from 4.30.3 to 4.31.0
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.30.3...4.31.0)

---
updated-dependencies:
- dependency-name: tox
  dependency-version: 4.31.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-11 10:01:51 +00:00
dependabot[bot]
08ce6499fc Bump the github-actions-dependencies group with 2 updates
Bumps the github-actions-dependencies group with 2 updates: [github/codeql-action](https://github.com/github/codeql-action) and [editorconfig-checker/action-editorconfig-checker](https://github.com/editorconfig-checker/action-editorconfig-checker).


Updates `github/codeql-action` from 3.30.6 to 4.30.8
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](64d10c1313...f443b600d9)

Updates `editorconfig-checker/action-editorconfig-checker` from f40bac9e7d9e7d298fbe36b83e1eff8f0de13fb8 to 1a41284d59c6fe7f1b21ddc4a2b36400a33dc1b4
- [Release notes](https://github.com/editorconfig-checker/action-editorconfig-checker/releases)
- [Commits](f40bac9e7d...1a41284d59)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 4.30.8
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions-dependencies
- dependency-name: editorconfig-checker/action-editorconfig-checker
  dependency-version: 1a41284d59c6fe7f1b21ddc4a2b36400a33dc1b4
  dependency-type: direct:production
  dependency-group: github-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-11 10:01:39 +00:00
Dominik
106b999054 Rename views, upgrade gravity database and bump gravity databae version (#6386) 2025-10-05 20:33:30 +02:00
yubiuser
564d917022 Bump the python-dependencies group across 1 directory with 3 updates (#6407) 2025-10-04 21:32:30 +02:00
yubiuser
a53dc8fa41 Bump the github-actions-dependencies group across 1 directory with 4 updates (#6409) 2025-10-04 21:30:30 +02:00
dependabot[bot]
37fc86410f Bump the github-actions-dependencies group across 1 directory with 4 updates
Bumps the github-actions-dependencies group with 4 updates in the / directory: [github/codeql-action](https://github.com/github/codeql-action), [actions/stale](https://github.com/actions/stale), [psf/black](https://github.com/psf/black) and [actions/setup-python](https://github.com/actions/setup-python).


Updates `github/codeql-action` from 3.29.10 to 3.30.6
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](96f518a34f...64d10c1313)

Updates `actions/stale` from 9.1.0 to 10.1.0
- [Release notes](https://github.com/actions/stale/releases)
- [Changelog](https://github.com/actions/stale/blob/main/CHANGELOG.md)
- [Commits](5bef64f19d...5f858e3efb)

Updates `psf/black` from 25.1.0 to 25.9.0
- [Release notes](https://github.com/psf/black/releases)
- [Changelog](https://github.com/psf/black/blob/main/CHANGES.md)
- [Commits](8a737e727a...af0ba72a73)

Updates `actions/setup-python` from 5.6.0 to 6.0.0
- [Release notes](https://github.com/actions/setup-python/releases)
- [Commits](a26af69be9...e797f83bcb)

---
updated-dependencies:
- dependency-name: github/codeql-action
  dependency-version: 3.30.6
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-dependencies
- dependency-name: actions/stale
  dependency-version: 10.1.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions-dependencies
- dependency-name: psf/black
  dependency-version: 25.9.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-dependencies
- dependency-name: actions/setup-python
  dependency-version: 6.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-04 19:26:34 +00:00
yubiuser
e0a9b06b8e Use SHA to pin github actions (#6392) 2025-10-04 21:25:09 +02:00
Christian König
f8d14c398e Use sha also fpr editor-config-checker without adding a specific tag commit
Signed-off-by: Christian König <github@yubiuser.dev>
2025-10-04 21:15:12 +02:00
dependabot[bot]
f5ce7b29e0 Bump the python-dependencies group across 1 directory with 3 updates
Bumps the python-dependencies group with 3 updates in the /test directory: [pyyaml](https://github.com/yaml/pyyaml), [pytest](https://github.com/pytest-dev/pytest) and [tox](https://github.com/tox-dev/tox).


Updates `pyyaml` from 6.0.2 to 6.0.3
- [Release notes](https://github.com/yaml/pyyaml/releases)
- [Changelog](https://github.com/yaml/pyyaml/blob/6.0.3/CHANGES)
- [Commits](https://github.com/yaml/pyyaml/compare/6.0.2...6.0.3)

Updates `pytest` from 8.4.1 to 8.4.2
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.1...8.4.2)

Updates `tox` from 4.28.4 to 4.30.2
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.28.4...4.30.2)

---
updated-dependencies:
- dependency-name: pyyaml
  dependency-version: 6.0.3
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
- dependency-name: pytest
  dependency-version: 8.4.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
- dependency-name: tox
  dependency-version: 4.30.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-10-04 10:02:17 +00:00
Dominik
c0a05297e6 Adjust .shellcheckrc to 0.11 and enable some optional checks (#6374) 2025-10-04 09:11:48 +02:00
Dominik
e5112a9fb8 Add Debian 13 Trixie to the test suite (#6382) 2025-10-04 09:10:30 +02:00
yubiuser
c31bfb8d82 Fix typo found during install (#6406) 2025-09-24 18:06:55 +02:00
Jon Herron
59ccfd6d13 Fix typo found during install
Signed-off-by: Jon Herron <jon.herron@yahoo.com>
2025-09-24 08:43:25 -04:00
Adam Warner
950107503d uninstall refactor and improvement (#6339) 2025-08-29 13:30:51 +01:00
Rob Gill
87f307f1d8 Uninstall refactor
Split removePiholeFiles into functions for each category.

Reorder execution so that:
  Pihole-FTL is stopped and removed before shutdown scripts are removed.
  User and group are removed before needed commands are removed.

Remove database and log files in user-specified non-default locations,
and use local directories from basic install.sh, falling back to defaults.

Remove use of sudo as the script already checks that it is called as root.

Advise user of location of crontab backup if is created

Make use of service control functions, command detection and default
directories from basic_install.sh

Align variable names with current basic-install.sh

Disable pihole-FTL service immediately, if systemctl is available

Call systemd daemon-reload after removing service files (on systemd systems)

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-08-29 08:47:00 +10:00
Adam Warner
0a837dd955 Alpine Linux Support and Tests (#6275) 2025-08-27 21:13:37 +01:00
Christian König
7140953500 Use SHA to pin github actions
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-27 21:02:18 +02:00
Michael Ziminsky (Z)
f50a4c1c89 Don't use hard-coded path for pihole user nologin shell
Signed-off-by: Michael Ziminsky (Z) <mgziminsky@users.noreply.github.com>
2025-08-16 21:47:57 +00:00
Michael Ziminsky (Z)
3908be911c Alpine: Switch cron service from busybox to cronie during install
Signed-off-by: Michael Ziminsky (Z) <mgziminsky@users.noreply.github.com>
2025-08-16 21:47:56 +00:00
Michael Ziminsky (Z)
44f95a4f57 Alpine: Ensure community repo is enabled and handle dependency install errors
Signed-off-by: Michael Ziminsky (Z) <mgziminsky@users.noreply.github.com>
2025-08-16 21:47:56 +00:00
Michael Ziminsky (Z)
4bf67a3c79 Alpine: Add some additional dependencies and minor script fixes
Signed-off-by: Michael Ziminsky (Z) <mgziminsky@users.noreply.github.com>
2025-08-16 21:47:55 +00:00
Rob Gill
d75dae788d Alpine Linux tests
Add Dockerfile and corresponding tox file to add alpine linux 3.21 & 3.22 to the test suite
Add apk as another package manager in the automated install tests
Use short switches for su command as busybox's su does not accept long switches here
Add Alpine test to github workflow

Signed-off-by: Michael Ziminsky (Z) <mgziminsky@users.noreply.github.com>
2025-08-16 21:47:54 +00:00
Michael Ziminsky (Z)
0db48383ae Add support for alpine
Signed-off-by: Michael Ziminsky (Z) <mgziminsky@users.noreply.github.com>
2025-08-16 21:47:54 +00:00
Dominik
73521bb110 Add FTL bash autocomplete (#6376) 2025-08-16 21:02:42 +02:00
yubiuser
36c4193dc5 Bump actions/checkout from 4.2.2 to 5.0.0 in the github-actions-dependencies group (#6387) 2025-08-16 12:36:52 +02:00
dependabot[bot]
6e9e961d3c Bump actions/checkout in the github-actions-dependencies group
Bumps the github-actions-dependencies group with 1 update: [actions/checkout](https://github.com/actions/checkout).


Updates `actions/checkout` from 4.2.2 to 5.0.0
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v4.2.2...v5.0.0)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: 5.0.0
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-16 10:23:35 +00:00
DL6ER
9e258e7005 Rename views, upgrade gravity database and bump gravity databae version
Signed-off-by: DL6ER <dl6er@dl6er.de>
2025-08-15 19:39:21 +02:00
Christian König
ea274073b4 Add Debian 13 Trixie to the test suite
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-13 06:27:19 +02:00
Christian König
94bbf5f429 Fix shellcheck warnings
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-07 19:44:26 +02:00
Christian König
4511daf560 Use pihole-FTL --complete to generate autocomplete suggestions
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-06 21:01:37 +02:00
Christian König
5d1ef6279f Fix shellcheck warnings for pihole bash completion
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-06 13:05:48 +02:00
Christian König
340ffbe5e8 Add pihole-FTL bash completion
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-06 13:05:36 +02:00
Christian König
7baa9c5de0 Adjust .shellcheckrc to 0.11 and enable some optional checks
Signed-off-by: Christian König <github@yubiuser.dev>
2025-08-04 20:38:27 +02:00
yubiuser
75286a1626 Bump tox from 4.28.3 to 4.28.4 in /test in the python-dependencies group (#6371) 2025-08-02 12:56:30 +02:00
dependabot[bot]
9bc17a1f2b Bump tox from 4.28.3 to 4.28.4 in /test in the python-dependencies group
Bumps the python-dependencies group in /test with 1 update: [tox](https://github.com/tox-dev/tox).


Updates `tox` from 4.28.3 to 4.28.4
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.28.3...4.28.4)

---
updated-dependencies:
- dependency-name: tox
  dependency-version: 4.28.4
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: python-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-08-02 10:52:42 +00:00
yubiuser
86850e9020 Add "setpassword" to pihole Bash completion (#6369) 2025-07-31 19:06:45 +02:00
deHakkelaar
d4562a1deb Add "setpassword" to pihole Bash completion
Its missing.
After:

```
$ sudo pihole
allow          arpflush       disable        logging        reloadlists    tail           version
allow-regex    checkout       enable         query          repair         uninstall      wildcard
allow-wild     debug          flush          regex          setpassword    updateGravity
api            deny           help           reloaddns      status         updatePihole
```

Signed-off-by: deHakkelaar <deHakkelaar@users.noreply.github.com>
2025-07-31 18:03:43 +02:00
Adam Warner
62ccfc77de Explicitly check for the existence of FTL binary before attempting to stop it. Prevents warning message on fresh installs (#6364) 2025-07-28 15:56:42 +01:00
Dominik
0df3b41ca0 Do not call pihole-FTL --config interactively (#6368) 2025-07-27 20:25:31 +02:00
DL6ER
5cb601200c Use non-interactive shell in utils.sh:getFTLConfigValue to avoid colored output
Signed-off-by: DL6ER <dl6er@dl6er.de>
2025-07-26 21:54:25 +02:00
Adam Warner
342c8b693b Revert "pihole api - use keepalive for curl queries" (#6367) 2025-07-26 19:12:46 +01:00
Adam Warner
24d8754033 Revert "pihole api - use keepalive for curl queries" 2025-07-26 12:35:02 +01:00
yubiuser
966e466c9e Bump tox from 4.27.0 to 4.28.3 in /test in the python-dependencies group (#6366) 2025-07-26 12:43:27 +02:00
dependabot[bot]
37ec67e9a3 Bump tox from 4.27.0 to 4.28.3 in /test in the python-dependencies group
Bumps the python-dependencies group in /test with 1 update: [tox](https://github.com/tox-dev/tox).


Updates `tox` from 4.27.0 to 4.28.3
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.27.0...4.28.3)

---
updated-dependencies:
- dependency-name: tox
  dependency-version: 4.28.3
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: python-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-26 10:36:36 +00:00
Adam Warner
364ba99b1f Update automated install/basic-install.sh
Co-authored-by: yubiuser <github@yubiuser.dev>
Signed-off-by: Adam Warner <github@adamwarner.co.uk>
2025-07-26 10:56:21 +01:00
Adam Warner
9252e90bd6 Check for the existence of the FTL binary before stopping the service
This prevents errors on fresh installs where the FTL binary does not exist yet.

Signed-off-by: Adam Warner <me@adamwarner.co.uk>
2025-07-26 10:31:58 +01:00
Adam Warner
ae0fef4626 pihole api - use keepalive for curl queries (#6365) 2025-07-26 10:22:16 +01:00
Rob Gill
5b4a7b8b74 pihole api - use keepalive for curl queries
Adds the keepalive header to all curl requests

This reduces session establishment time across the multiple

requests necessary to authenticate, obtain response and log out

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-07-26 18:48:32 +10:00
Dan Schaper
a9680db218 Use 'true'/'false' strings instead of 0/1 integers for boolean root user check in pihole command (#6351) 2025-07-23 09:16:15 -07:00
Christian König
7aa57e154e Group functions
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-23 08:23:09 +02:00
Christian König
05f4ae7719 Invert need_root logic and check if set/unset
Signed-off-by: Christian König <github@yubiuser.dev>
Co-authored-by: Dan Schaper <dan.schaper@pi-hole.net>
2025-07-23 08:09:26 +02:00
Dan Schaper
29b6252935 Fix gravity user permission checking and fix error on pihole-FTL --config failures (#6352) 2025-07-22 15:27:07 -07:00
Dan Schaper
f324d4cc7d Separate network flush and arp table flush functions (#6319)
This PR updates the function flushing the network tables and the ARP table. It separates the two, renaming the whole function to networkflush and makes flushing ARP optional by appending --arp. Deletion of the network table is now done via FTL's /action/flush/arp endpoint.

Documentation: https://github.com/pi-hole/docs/pull/1253
Related PR in the FTL repo: https://github.com/pi-hole/FTL/pull/2541
2025-07-22 15:20:50 -07:00
yubiuser
310f0711fa Group evaluations
Co-authored-by: Dan Schaper <dan.schaper@pi-hole.net>
Signed-off-by: yubiuser <github@yubiuser.dev>
2025-07-22 12:52:43 +02:00
Christian König
9580dc6560 Improve setFTLConfigValue function
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-22 12:50:42 +02:00
Christian König
70e317f373 Revert https://github.com/pi-hole/pi-hole/pull/6345
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-22 12:50:42 +02:00
Christian König
285b3c37f9 Tweak fluash ARP function
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-22 12:45:22 +02:00
Dan Schaper
41f6738e97 Update man page - remove "reconfigure" option (#6361) 2025-07-21 13:57:51 -07:00
RD WebDesign
22e6ff2cdf Update man page - remove "reconfigure" option
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-07-21 16:15:25 -03:00
Dan Schaper
be36432f24 Speed up pihole api (#6336) 2025-07-21 10:51:25 -07:00
Dan Schaper
4bea9a1d12 Use RTMIN value provided by FTL when possible (#6358)
In some cases `kill -RTMIN` can send incorrect signal value to the FTL causing it to terminate instead of performing lists reload.
https://github.com/pi-hole/FTL/issues/2573
2025-07-20 18:06:00 -07:00
Dan Schaper
35ce3580e5 Reduce pihole --query jq calls (#6334) 2025-07-20 18:02:45 -07:00
Dan Schaper
90eb5778ca Re-order authentication errors in verbose mode (#6338) 2025-07-20 17:46:07 -07:00
Dan Schaper
c0539a4fb0 Remove netcat from the list of dependencies (#6343) 2025-07-20 17:44:04 -07:00
Jack'lul
da85a7a2a7 Use RTMIN value provided by FTL when possible
Signed-off-by: Jack'lul <jacklulcat@gmail.com>
2025-07-18 23:13:19 +02:00
Christian König
3c52ce98d4 Revert https://github.com/pi-hole/pi-hole/pull/6312
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-16 13:50:37 +02:00
Dan Schaper
ae048201a2 Remove readonly from list.sh to avoid errors (#6349) 2025-07-14 21:10:53 -07:00
RD WebDesign
dad27f8d06 Remove readonly from list.sh to avoid errors
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-07-14 21:20:39 -03:00
Dan Schaper
c0b512be60 Sync master back into development (#6348) 2025-07-14 14:45:31 -07:00
Adam Warner
cef7fd4b02 Pi-hole core 6.1.4 (#6347) 2025-07-14 22:15:50 +01:00
Adam Warner
c09da77e2b Fix pihole api command by not setting the some variabes as readonly (#6346) 2025-07-14 22:09:27 +01:00
Adam Warner
18e8396a44 Fix issue where web interface cannot run gravity (#6345) 2025-07-14 22:05:18 +01:00
Adam Warner
1bef0415db bare minimum first past fix for pihoe api command not working due to attempting to re-set readony variabes PI_HOE_SCRIPT_DIR and utisfie.
Can't compain about them being readony if they're.. not.

Signed-off-by: Adam Warner <me@adamwarner.co.uk>
2025-07-14 22:02:02 +01:00
Adam Warner
d6a83baf4f Allow non-root access for updateGravity command
Signed-off-by: Adam Warner <me@adamwarner.co.uk>
2025-07-14 21:38:59 +01:00
Dan Schaper
605ff8183a Sync master back into development (#6341) 2025-07-14 11:45:03 -07:00
darkexplosiveqwx
e231107e81 Remove netcat from the list of dependencies
Signed-off-by: darkexplosiveqwx <101737077+darkexplosiveqwx@users.noreply.github.com>
2025-07-14 20:18:18 +02:00
Dan Schaper
9494dc6061 Pi-hole Core v6.1.3 (#6337) 2025-07-14 07:14:43 -07:00
Christian König
71501e15e5 Re-order authentication errors in verbose mode
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-13 22:16:40 +02:00
Rob Gill
0187087da0 Speed up pihole --api
Get session authentication information via single jq operation,
setting defaults if no data returned.

Simplify jq test for valid JSON data

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-07-14 05:44:46 +10:00
Dan Schaper
9605ccc178 basic-install.sh listing interfaces (#6269) 2025-07-13 11:57:32 -07:00
deHakkelaar
86bdae0076 Update basic-install.sh
Added comments
2025-07-13 11:05:58 +02:00
Rob Gill
05ad3d7155 Update advanced/Scripts/query.sh
Co-authored-by: yubiuser <github@yubiuser.dev>
Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-07-12 22:25:22 +00:00
Rob Gill
ea8272d7d4 speed up pihole --query
Count list and gravity matches using jq in a single step.

Use jq's map to simplify list processing, eliminating intermediate
jsons.

Eliminate while loop for each lists's final output and formatting.

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-07-12 14:25:17 +10:00
Dan Schaper
1e88ce4975 piholeDebug - Get default route robustly (#6303) 2025-07-11 16:19:54 -07:00
Adam Warner
bb60e2e346 Sync master back into development (#6332) 2025-07-11 21:02:14 +01:00
Adam Warner
3977a312f0 Update release.yml
Signed-off-by: Adam Warner <me@adamwarner.co.uk>
2025-07-11 21:01:01 +01:00
Dan Schaper
b3d193b34c Update Firewalld tests to match v6 ports (#6307) 2025-07-10 14:15:41 -07:00
Dan Schaper
0a36e9d949 update: abort if FTL branch does not exist (#6329) 2025-07-10 12:55:12 -07:00
MichaIng
6db6c68a4e update: abort if FTL branch does not exist
Currently, if the FTL update check returns 404, hence the FTL branch does not seem to exist, an error message is printed, but the update continues, only the FTL update is skipped.

This can lead to setups with v5 FTL and v6 core/web, failing at config migration, where FTL is invoked with a v6-only command.

With this change, the update aborts immediately if the FTL branch is invalid, like it does in case of other FTL update check errors (other HTTP error codes than 404 or other curl errors). Hence it continues only if FTL is up-to-date already, or a new version from the given branch has been found.

Signed-off-by: MichaIng <micha@dietpi.com>
2025-07-10 21:37:28 +02:00
Dan Schaper
5b5e02d492 Do not skip root check for pihole user (#6312) 2025-07-10 11:48:29 -07:00
Dan Schaper
a2dd0e2f50 Prevent gravity curl from using unset bash variable's empty string. (#6191) 2025-07-10 11:29:00 -07:00
Dan Schaper
3d75ea6df2 Only update the package cache on fresh installations (#6282) 2025-07-10 11:20:03 -07:00
Dan Schaper
080e1ccf8a installer: exit if FTL update check fails (#6316) 2025-07-10 10:59:05 -07:00
Dan Schaper
24f32d48af Fix pihole -q by setting PI_HOLE_SCRIPT_DIR in api.sh (#6284) 2025-07-10 10:51:20 -07:00
Dan Schaper
8486cfd95a Update python version used in test to 3.13 (#6321) 2025-07-10 10:47:49 -07:00
yubiuser
c81d5019a3 Bump pytest-xdist from 3.7.0 to 3.8.0 in /test (#6324) 2025-07-05 23:07:49 +02:00
dependabot[bot]
c5c5116e53 Bump pytest-xdist from 3.7.0 to 3.8.0 in /test
Bumps [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) from 3.7.0 to 3.8.0.
- [Release notes](https://github.com/pytest-dev/pytest-xdist/releases)
- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v3.7.0...v3.8.0)

---
updated-dependencies:
- dependency-name: pytest-xdist
  dependency-version: 3.8.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-07-05 10:28:30 +00:00
Christian König
2f9fa80d7a Update python version used in test to 3.13
Signed-off-by: Christian König <github@yubiuser.dev>
2025-07-01 13:34:55 +02:00
Dominik
f2280eb330 taillog Prevent grep interpeting search term as an option (#6318) 2025-06-29 07:08:21 +02:00
Rob Gill
f24fc9573a taillog Prevent grep interpeting search term as an option
Adds '--' indicating end of options before the user provided
search pattern.

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-06-29 06:45:02 +10:00
MichaIng
3a35e589f2 installer: exit if FTL update check fails
The return code of `FTLdetect()` is used in the installer to know whether FTL has been installed or not.

The function however returns an error only, if the download of FTL fails, not if checking for a latest version/update of FTL fails. This way, installs and rapairs can continue without or with ourdated FTL until `pihole-FTL migrate v6`, which hangs endlessly, if it is a v5 FTL.

This commit handles the return code in `FTLdetect()`, and lets it return true only if FTL download succeeded, or if the update check succeeded and FTL is up-to-date. Else, it could neither be repaired, nor installed, and the error message should give a hint what went wrong, hence exit.

`FTLdetect()` is not called by any other script, hence this change has no surprising effect elsewhere.

Additionally, a syntax error in the `FTLcheckUpdate()` function itself is fixed, which masks the `check_download_exists()` return code, hence always leads to error code 4, if the FTL branch is not `master`.

Signed-off-by: MichaIng <micha@dietpi.com>
2025-06-26 00:53:49 +02:00
Rob Gill
19d5943440 piholeDebug - Get default route robustly
Determine address and interface of default route by preceeding
    'via' and 'dev' fields in json output instead of plain text
    field position.

    Log if unable to determine default gateway

Signed-off-by: Rob Gill <rrobgill@protonmail.com>
2025-06-25 05:10:12 +10:00
Dominik
f90677a1ff Clean COL_TABLE and allow to send color codes when invoked by FTL (${FORCE_COLOR}) (#6314) 2025-06-24 19:24:40 +02:00
Christian König
daec6f8c02 Set color codes when FORCE_COLOR is true
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-24 19:03:24 +02:00
Christian König
a48665c7bb Remove deprecated and unused colors
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-24 18:51:01 +02:00
Adam Warner
8a97a1433a Fix dependabot (#6297) 2025-06-22 14:28:08 +02:00
Adam Warner
f94d9f2540 Add note about adding local user to pihole group to final dialog (#6152) 2025-06-22 14:13:46 +02:00
Christian König
830c4bc049 Do not skipp root check for pihole user
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-21 22:40:43 +02:00
yubiuser
1dbe425146 Bump pytest from 8.4.0 to 8.4.1 in /test (#6311) 2025-06-21 14:31:07 +02:00
yubiuser
fc2f7795e9 Bump tox from 4.26.0 to 4.27.0 in /test (#6310) 2025-06-21 14:30:38 +02:00
dependabot[bot]
0bc06ed204 Bump pytest from 8.4.0 to 8.4.1 in /test
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.4.0 to 8.4.1.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.4.0...8.4.1)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.1
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-21 10:10:47 +00:00
dependabot[bot]
700c892dff Bump tox from 4.26.0 to 4.27.0 in /test
Bumps [tox](https://github.com/tox-dev/tox) from 4.26.0 to 4.27.0.
- [Release notes](https://github.com/tox-dev/tox/releases)
- [Changelog](https://github.com/tox-dev/tox/blob/main/docs/changelog.rst)
- [Commits](https://github.com/tox-dev/tox/compare/4.26.0...4.27.0)

---
updated-dependencies:
- dependency-name: tox
  dependency-version: 4.27.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-21 10:10:43 +00:00
RD WebDesign
405053692a Add HTTPS and NTP services to firewalld test
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-06-17 21:32:49 -03:00
RD WebDesign
04d9d32444 Remove test for port 4711
Signed-off-by: RD WebDesign <github@rdwebdesign.com.br>
2025-06-17 20:19:13 -03:00
Christian König
c19e907c0c Fix dependabot
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-09 19:06:15 +02:00
Christian König
7a16024020 Run package update everytime before building the meta package when invoking from the install script
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-08 21:31:56 +02:00
Christian König
5777497f52 Separate package manager detection and cache update functions
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-08 21:25:43 +02:00
Adam Warner
404dc7cb88 Add 'never-stale' to the exempt issue labels of the stale workflow (#6283) 2025-06-08 15:06:33 +01:00
Adam Warner
2f3dfef862 Dependabot: group updates (#6294) 2025-06-07 18:36:35 +01:00
XhmikosR
b39c9956e8 Dependabot: group updates
Signed-off-by: XhmikosR <xhmikosr@gmail.com>
2025-06-07 18:27:46 +03:00
yubiuser
70bcb0bb2b Bump pytest-xdist from 3.6.1 to 3.7.0 in /test (#6254) 2025-06-07 12:32:36 +02:00
dependabot[bot]
2c1032090d Bump pytest-xdist from 3.6.1 to 3.7.0 in /test
Bumps [pytest-xdist](https://github.com/pytest-dev/pytest-xdist) from 3.6.1 to 3.7.0.
- [Release notes](https://github.com/pytest-dev/pytest-xdist/releases)
- [Changelog](https://github.com/pytest-dev/pytest-xdist/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest-xdist/compare/v3.6.1...v3.7.0)

---
updated-dependencies:
- dependency-name: pytest-xdist
  dependency-version: 3.7.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-07 10:07:56 +00:00
yubiuser
b5adc981c2 Bump pytest from 8.3.5 to 8.4.0 in /test (#6293) 2025-06-07 12:06:51 +02:00
dependabot[bot]
3933cb0575 Bump pytest from 8.3.5 to 8.4.0 in /test
Bumps [pytest](https://github.com/pytest-dev/pytest) from 8.3.5 to 8.4.0.
- [Release notes](https://github.com/pytest-dev/pytest/releases)
- [Changelog](https://github.com/pytest-dev/pytest/blob/main/CHANGELOG.rst)
- [Commits](https://github.com/pytest-dev/pytest/compare/8.3.5...8.4.0)

---
updated-dependencies:
- dependency-name: pytest
  dependency-version: 8.4.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-06-07 10:01:39 +00:00
Dan Schaper
6964cd124e Sync master back into development (#6291) 2025-06-06 16:22:37 -07:00
Christian König
d16c049768 Set PI_HOLE_SCRIPT_DIR in api.sh
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-05 14:45:29 +02:00
Christian König
5ff4f000d5 Add 'never-stale' to the exempt issue labels of the stale workflow
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-05 14:16:27 +02:00
Christian König
93ecfb9504 We test a fresh installaton, so don't pretend this is not a fresh installation
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-05 13:09:03 +02:00
Christian König
ea61755881 Only update the package cache on fresh installations
Signed-off-by: Christian König <github@yubiuser.dev>
2025-06-05 12:50:10 +02:00
deHakkelaar
6130b800e3 basic-install.sh listing interfaces
Following up on below one:

https://github.com/pi-hole/pi-hole/pull/6236

And below poor attempt:

https://github.com/pi-hole/pi-hole/pull/6256

Signed-off-by: deHakkelaar <deHakkelaar@users.noreply.github.com>
2025-06-02 03:59:36 +02:00
Dan Schaper
ec892ec096 Pi-hole Core v6.1.1 (#6267) 2025-06-01 18:35:19 -07:00
Dan Schaper
4d4195ed4e Revert "Use PID1 to determine which command to use when toggeling services" (#6262) 2025-06-01 09:45:48 -07:00
Dan Schaper
260fa5ea40 Sync master back into development (#6253) 2025-06-01 09:15:10 -07:00
yubiuser
03b3b69617 Make LOGFILE readonly after assignment (#6261) 2025-06-01 15:47:11 +02:00
Dan Schaper
8c81335004 Don't revert the package metadata
Signed-off-by: Dan Schaper <dan.schaper@pi-hole.net>
2025-05-31 11:39:13 -07:00
Dan Schaper
e1b05028a7 Revert "Use PID1 to determine which command to use when toggeling services" 2025-05-31 09:14:08 -07:00
Dan Schaper
89c4976da4 Make LOGFILE readonly after assignment
Signed-off-by: Dan Schaper <dan.schaper@pi-hole.net>
2025-05-31 08:23:24 -07:00
Adam Warner
4fed49c5e5 Pi-hole Core v6.1 (#6221) 2025-05-30 22:56:55 +01:00
Dan Schaper
9aa005ad4c Revert "fix(get_available_interfaces): only drop the loopback device (‘lo’), don’t filter every “lo” substring" (#6235) 2025-05-14 09:53:07 -07:00
Dan Schaper
f6d477f228 Revert "fix(get_available_interfaces): only drop the loopback device (‘lo’), don’t filter every “lo” substring" 2025-05-14 09:51:47 -07:00
Dan Schaper
3c1d8690fa fix(get_available_interfaces): only drop the loopback device (‘lo’), don’t filter every “lo” substring (#6224) 2025-05-14 09:05:21 -07:00
Christian König
a590b77431 Link to documentation on how to add local user to pihole group
Signed-off-by: Christian König <github@yubiuser.dev>
2025-05-14 09:36:52 +02:00
Piotr Tyrakowski
13848f2da1 Update automated install/basic-install.sh
Co-authored-by: Dan Schaper <dan.schaper@pi-hole.net>
Signed-off-by: Piotr Tyrakowski <verscup@gmail.com>
2025-05-12 21:13:01 +02:00
Piotr Tyrakowski
9f3e71b0b6 Update basic-install.sh
I have updated line 708
to use grep -v "loo" 
instead of "lo" 
the reason is with "lo" it cannot find "wlo1" interface

Signed-off-by: Piotr Tyrakowski <verscup@gmail.com>
2025-05-09 17:03:57 +02:00
Christian König
95d27ac985 Merge branch 'development' into tweak/gravity_options
Signed-off-by: Christian König <github@yubiuser.dev>
2025-05-04 14:17:41 +02:00
Christian König
7a641f4c35 Use paramteter expansion to prevent adding literal '' if parameter is empty
Signed-off-by: Christian König <github@yubiuser.dev>
2025-04-22 22:52:33 +02:00
Christian König
13d76abff7 Set customUpstreamResolver empty
Signed-off-by: Christian König <github@yubiuser.dev>
2025-04-22 22:38:07 +02:00
Christian König
76e41aeefa Add small note about modifiedOptions
Signed-off-by: Christian König <github@yubiuser.dev>
2025-04-22 22:03:54 +02:00
Christian König
774037834b Rename cmd_ext
Signed-off-by: Christian König <github@yubiuser.dev>
2025-04-22 22:01:21 +02:00
Christian König
89c4248315 Use quotes for all substitutions
Signed-off-by: Christian König <github@yubiuser.dev>
2025-04-22 21:57:47 +02:00
Michael Woolweaver
6fc5bf83f4 don't mute SC2086
Signed-off-by: Michael Woolweaver <michael@woolweaver.bid>
2025-04-22 21:56:21 +02:00
45 changed files with 939 additions and 555 deletions

View File

@@ -8,6 +8,10 @@ updates:
time: "10:00" time: "10:00"
open-pull-requests-limit: 10 open-pull-requests-limit: 10
target-branch: development target-branch: development
groups:
github-actions-dependencies:
patterns:
- "*"
- package-ecosystem: pip - package-ecosystem: pip
directory: "/test" directory: "/test"
schedule: schedule:
@@ -16,3 +20,7 @@ updates:
time: "10:00" time: "10:00"
open-pull-requests-limit: 10 open-pull-requests-limit: 10
target-branch: development target-branch: development
groups:
python-dependencies:
patterns:
- "*"

1
.github/release.yml vendored
View File

@@ -2,6 +2,7 @@ changelog:
exclude: exclude:
labels: labels:
- internal - internal
- dependencies
authors: authors:
- dependabot - dependabot
- github-actions - github-actions

View File

@@ -25,16 +25,16 @@ jobs:
steps: steps:
- -
name: Checkout repository name: Checkout repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
# Initializes the CodeQL tools for scanning. # Initializes the CodeQL tools for scanning.
- -
name: Initialize CodeQL name: Initialize CodeQL
uses: github/codeql-action/init@v3 uses: github/codeql-action/init@f443b600d91635bebf5b0d9ebc620189c0d6fba5 #v4.30.8
with: with:
languages: 'python' languages: 'python'
- -
name: Autobuild name: Autobuild
uses: github/codeql-action/autobuild@v3 uses: github/codeql-action/autobuild@f443b600d91635bebf5b0d9ebc620189c0d6fba5 #v4.30.8
- -
name: Perform CodeQL Analysis name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v3 uses: github/codeql-action/analyze@f443b600d91635bebf5b0d9ebc620189c0d6fba5 #v4.30.8

View File

@@ -13,7 +13,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Check if PRs are have merge conflicts - name: Check if PRs are have merge conflicts
uses: eps1lon/actions-label-merge-conflict@v3.0.3 uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 #v3.0.3
with: with:
dirtyLabel: "PR: Merge Conflict" dirtyLabel: "PR: Merge Conflict"
repoToken: "${{ secrets.GITHUB_TOKEN }}" repoToken: "${{ secrets.GITHUB_TOKEN }}"

View File

@@ -17,14 +17,14 @@ jobs:
issues: write issues: write
steps: steps:
- uses: actions/stale@v9.1.0 - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 #v10.1.0
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
days-before-stale: 30 days-before-stale: 30
days-before-close: 5 days-before-close: 5
stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.' stale-issue-message: 'This issue is stale because it has been open 30 days with no activity. Please comment or update this issue or it will be closed in 5 days.'
stale-issue-label: '${{ env.stale_label }}' stale-issue-label: '${{ env.stale_label }}'
exempt-issue-labels: 'Internal, Fixed in next release, Bug: Confirmed, Documentation Needed' exempt-issue-labels: 'Internal, Fixed in next release, Bug: Confirmed, Documentation Needed, never-stale'
exempt-all-issue-assignees: true exempt-all-issue-assignees: true
operations-per-run: 300 operations-per-run: 300
close-issue-reason: 'not_planned' close-issue-reason: 'not_planned'
@@ -40,7 +40,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
- name: Remove 'stale' label - name: Remove 'stale' label
run: gh issue edit ${{ github.event.issue.number }} --remove-label ${{ env.stale_label }} run: gh issue edit ${{ github.event.issue.number }} --remove-label ${{ env.stale_label }}
env: env:

View File

@@ -17,7 +17,7 @@ jobs:
pull-requests: write pull-requests: write
steps: steps:
- uses: actions/stale@v9.1.0 - uses: actions/stale@5f858e3efba33a5ca4407a664cc011ad407f2008 #v10.1.0
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
# Do not automatically mark PR/issue as stale # Do not automatically mark PR/issue as stale

View File

@@ -33,7 +33,7 @@ jobs:
name: Syncing branches name: Syncing branches
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
- name: Opening pull request - name: Opening pull request
run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal' run: gh pr create -B development -H master --title 'Sync master back into development' --body 'Created by Github action' --label 'internal'
env: env:

View File

@@ -18,7 +18,7 @@ jobs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
with: with:
fetch-depth: 0 # Differential ShellCheck requires full git history fetch-depth: 0 # Differential ShellCheck requires full git history
@@ -31,25 +31,25 @@ jobs:
[[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!" [[ $FAIL == 1 ]] && exit 1 || echo "Scripts are executable!"
- name: Differential ShellCheck - name: Differential ShellCheck
uses: redhat-plumbers-in-action/differential-shellcheck@v5 uses: redhat-plumbers-in-action/differential-shellcheck@0d9e5b29625f871e6a4215380486d6f1a7cb6cdd #v5.5.5
with: with:
severity: warning severity: warning
display-engine: sarif-fmt display-engine: sarif-fmt
- name: Spell-Checking - name: Spell-Checking
uses: codespell-project/actions-codespell@master uses: codespell-project/actions-codespell@406322ec52dd7b488e48c1c4b82e2a8b3a1bf630 #v2.1
with: with:
ignore_words_file: .codespellignore ignore_words_file: .codespellignore
- name: Get editorconfig-checker - name: Get editorconfig-checker
uses: editorconfig-checker/action-editorconfig-checker@main # tag v1.0.0 is really out of date uses: editorconfig-checker/action-editorconfig-checker@1a41284d59c6fe7f1b21ddc4a2b36400a33dc1b4 # tag v2. is really out of date
- name: Run editorconfig-checker - name: Run editorconfig-checker
run: editorconfig-checker run: editorconfig-checker
- name: Check python code formatting with black - name: Check python code formatting with black
uses: psf/black@stable uses: psf/black@af0ba72a73598c76189d6dd1b21d8532255d5942 #25.9.0
with: with:
src: "./test" src: "./test"
options: "--check --diff --color" options: "--check --diff --color"
@@ -65,6 +65,7 @@ jobs:
[ [
debian_11, debian_11,
debian_12, debian_12,
debian_13,
ubuntu_20, ubuntu_20,
ubuntu_22, ubuntu_22,
ubuntu_24, ubuntu_24,
@@ -73,17 +74,19 @@ jobs:
fedora_40, fedora_40,
fedora_41, fedora_41,
fedora_42, fedora_42,
alpine_3_21,
alpine_3_22,
] ]
env: env:
DISTRO: ${{matrix.distro}} DISTRO: ${{matrix.distro}}
steps: steps:
- name: Checkout repository - name: Checkout repository
uses: actions/checkout@v4.2.2 uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 #v5.0.0
- name: Set up Python 3.10 - name: Set up Python
uses: actions/setup-python@v5.6.0 uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c #v6.0.0
with: with:
python-version: "3.10" python-version: "3.13"
- name: Install wheel - name: Install wheel
run: pip install wheel run: pip install wheel

View File

@@ -1,2 +1,6 @@
external-sources=true # allow shellcheck to read external sources external-sources=true # allow shellcheck to read external sources
disable=SC3043 #disable SC3043: In POSIX sh, local is undefined. disable=SC3043 #disable SC3043: In POSIX sh, local is undefined.
enable=useless-use-of-cat # disabled by default as of shellcheck 0.11.0
enable=avoid-negated-conditions # avoid-negated-conditions is optional as of shellcheck 0.11.0
enable=require-variable-braces
enable=deprecate-which

View File

@@ -1,11 +1,12 @@
#!/usr/bin/env sh #!/usr/bin/env sh
# shellcheck disable=SC2034 # Disable warning about unused variables
# Determine if terminal is capable of showing colors # Determine if terminal is capable of showing colors
if [ -t 1 ] && [ "$(tput colors)" -ge 8 ]; then # When COL_TABLE is sourced via gravity invoked by FTL, FORCE_COLOR is set to true
if { [ -t 1 ] && [ "$(tput colors)" -ge 8 ]; } || [ "${FORCE_COLOR}" ]; then
# Bold and underline may not show up on all clients # Bold and underline may not show up on all clients
# If something MUST be emphasized, use both # If something MUST be emphasized, use both
COL_BOLD='' COL_BOLD=''
COL_ULINE=''
COL_NC='' COL_NC=''
COL_GRAY='' COL_GRAY=''
COL_RED='' COL_RED=''
@@ -17,8 +18,6 @@ if [ -t 1 ] && [ "$(tput colors)" -ge 8 ]; then
else else
# Provide empty variables for `set -u` # Provide empty variables for `set -u`
COL_BOLD="" COL_BOLD=""
COL_ULINE=""
COL_NC="" COL_NC=""
COL_GRAY="" COL_GRAY=""
COL_RED="" COL_RED=""
@@ -29,22 +28,8 @@ else
COL_CYAN="" COL_CYAN=""
fi 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}]" TICK="[${COL_GREEN}✓${COL_NC}]"
CROSS="[${COL_RED}✗${COL_NC}]" CROSS="[${COL_RED}✗${COL_NC}]"
INFO="[i]" INFO="[i]"
QST="[?]" QST="[?]"
DONE="${COL_GREEN} done!${COL_NC}"
OVER="\\r" OVER="\\r"

View File

@@ -22,7 +22,8 @@ TestAPIAvailability() {
local chaos_api_list authResponse authStatus authData apiAvailable DNSport local chaos_api_list authResponse authStatus authData apiAvailable DNSport
# as we are running locally, we can get the port value from FTL directly # as we are running locally, we can get the port value from FTL directly
readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" PI_HOLE_SCRIPT_DIR="/opt/pihole"
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
# shellcheck source=./advanced/Scripts/utils.sh # shellcheck source=./advanced/Scripts/utils.sh
. "${utilsfile}" . "${utilsfile}"
@@ -149,7 +150,6 @@ LoginAPI() {
# Try to login again until the session is valid # Try to login again until the session is valid
while [ ! "${validSession}" = true ] ; do while [ ! "${validSession}" = true ] ; do
echo "Authentication failed. Please enter your Pi-hole password"
# Print the error message if there is one # Print the error message if there is one
if [ ! "${sessionError}" = "null" ] && [ "${1}" = "verbose" ]; then if [ ! "${sessionError}" = "null" ] && [ "${1}" = "verbose" ]; then
@@ -160,6 +160,14 @@ LoginAPI() {
echo "Error: ${sessionMessage}" echo "Error: ${sessionMessage}"
fi fi
if [ "${1}" = "verbose" ]; then
# If we are not in verbose mode, no need to print the error message again
echo "Please enter your Pi-hole password"
else
echo "Authentication failed. Please enter your Pi-hole password"
fi
# secretly read the password # secretly read the password
secretRead; printf '\n' secretRead; printf '\n'
@@ -182,13 +190,20 @@ Authentication() {
echo "No response from FTL server. Please check connectivity" echo "No response from FTL server. Please check connectivity"
exit 1 exit 1
fi fi
# obtain validity, session ID and sessionMessage from session response
validSession=$(echo "${sessionResponse}"| jq .session.valid 2>/dev/null)
SID=$(echo "${sessionResponse}"| jq --raw-output .session.sid 2>/dev/null)
sessionMessage=$(echo "${sessionResponse}"| jq --raw-output .session.message 2>/dev/null)
# obtain the error message from the session response # obtain validity, session ID, sessionMessage and error message from
sessionError=$(echo "${sessionResponse}"| jq --raw-output .error.message 2>/dev/null) # session response, apply default values if none returned
result=$(echo "${sessionResponse}" | jq -r '
(.session.valid // false),
(.session.sid // null),
(.session.message // null),
(.error.message // null)
' 2>/dev/null)
validSession=$(echo "${result}" | sed -n '1p')
SID=$(echo "${result}" | sed -n '2p')
sessionMessage=$(echo "${result}" | sed -n '3p')
sessionError=$(echo "${result}" | sed -n '4p')
if [ "${1}" = "verbose" ]; then if [ "${1}" = "verbose" ]; then
if [ "${validSession}" = true ]; then if [ "${validSession}" = true ]; then
@@ -352,12 +367,9 @@ apiFunc() {
if [ "${verbosity}" = "verbose" ]; then if [ "${verbosity}" = "verbose" ]; then
echo "Data:" echo "Data:"
fi fi
# Attempt to print the data with jq, if it is not valid JSON, or not installed
if command -v jq >/dev/null && echo "${data}" | jq . >/dev/null 2>&1; then # then print the plain text.
echo "${data}" | jq . echo "${data}" | jq . 2>/dev/null || echo "${data}"
else
echo "${data}"
fi
# Delete the session # Delete the session
LogoutAPI "${verbosity}" LogoutAPI "${verbosity}"

View File

@@ -150,4 +150,10 @@ upgrade_gravityDB(){
pihole-FTL sqlite3 -ni "${database}" < "${scriptPath}/18_to_19.sql" pihole-FTL sqlite3 -ni "${database}" < "${scriptPath}/18_to_19.sql"
version=19 version=19
fi fi
if [[ "$version" == "19" ]]; then
# Update views to use new allowlist/denylist names
echo -e " ${INFO} Upgrading gravity database from version 19 to 20"
pihole-FTL sqlite3 -ni "${database}" < "${scriptPath}/19_to_20.sql"
version=20
fi
} }

View File

@@ -0,0 +1,43 @@
.timeout 30000
BEGIN TRANSACTION;
DROP VIEW vw_whitelist;
CREATE VIEW vw_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 0
ORDER BY domainlist.id;
DROP VIEW vw_blacklist;
CREATE VIEW vw_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 1
ORDER BY domainlist.id;
DROP VIEW vw_regex_whitelist;
CREATE VIEW vw_regex_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 2
ORDER BY domainlist.id;
DROP VIEW vw_regex_blacklist;
CREATE VIEW vw_regex_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
WHERE domainlist.enabled = 1 AND (domainlist_by_group.group_id IS NULL OR "group".enabled = 1)
AND domainlist.type = 3
ORDER BY domainlist.id;
UPDATE info SET value = 20 WHERE property = 'version';
COMMIT;

View File

@@ -9,12 +9,12 @@
# 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_SCRIPT_DIR="/opt/pihole" PI_HOLE_SCRIPT_DIR="/opt/pihole"
readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
# shellcheck source="./advanced/Scripts/utils.sh" # shellcheck source="./advanced/Scripts/utils.sh"
source "${utilsfile}" source "${utilsfile}"
readonly apifile="${PI_HOLE_SCRIPT_DIR}/api.sh" apifile="${PI_HOLE_SCRIPT_DIR}/api.sh"
# shellcheck source="./advanced/Scripts/api.sh" # shellcheck source="./advanced/Scripts/api.sh"
source "${apifile}" source "${apifile}"

View File

@@ -1,90 +0,0 @@
#!/usr/bin/env bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# ARP table interaction
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then
# shellcheck source="./advanced/Scripts/COL_TABLE"
source ${coltable}
fi
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
# shellcheck source=./advanced/Scripts/utils.sh
source "${utilsfile}"
readonly PI_HOLE_FILES_DIR="/etc/.pihole"
SKIP_INSTALL="true"
# shellcheck source="./automated install/basic-install.sh"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# stop_service() is defined in basic-install.sh
# restart_service() is defined in basic-install.sh
# Determine database location
DBFILE=$(getFTLConfigValue "files.database")
if [ -z "$DBFILE" ]; then
DBFILE="/etc/pihole/pihole-FTL.db"
fi
flushARP(){
local output
if [[ "${args[1]}" != "quiet" ]]; then
echo -ne " ${INFO} Flushing network table ..."
fi
# Stop FTL to prevent database access
if ! output=$(stop_service pihole-FTL 2>&1); then
echo -e "${OVER} ${CROSS} Failed to stop FTL"
echo " Output: ${output}"
return 1
fi
# Truncate network_addresses table in pihole-FTL.db
# This needs to be done before we can truncate the network table due to
# foreign key constraints
if ! output=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM network_addresses" 2>&1); then
echo -e "${OVER} ${CROSS} Failed to truncate network_addresses table"
echo " Database location: ${DBFILE}"
echo " Output: ${output}"
return 1
fi
# Truncate network table in pihole-FTL.db
if ! output=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM network" 2>&1); then
echo -e "${OVER} ${CROSS} Failed to truncate network table"
echo " Database location: ${DBFILE}"
echo " Output: ${output}"
return 1
fi
# Flush ARP cache of the host
if ! output=$(ip -s -s neigh flush all 2>&1); then
echo -e "${OVER} ${CROSS} Failed to flush ARP cache"
echo " Output: ${output}"
return 1
fi
# Start FTL again
if ! output=$(restart_service pihole-FTL 2>&1); then
echo -e "${OVER} ${CROSS} Failed to restart FTL"
echo " Output: ${output}"
return 1
fi
if [[ "${args[1]}" != "quiet" ]]; then
echo -e "${OVER} ${TICK} Flushed network table"
fi
}
args=("$@")
case "${args[0]}" in
"arpflush" ) flushARP;;
esac

View File

@@ -26,7 +26,7 @@ source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
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 " ${COL_LIGHT_RED}This feature is NOT supported unless a Pi-hole developer explicitly asks!${COL_NC}" echo -e " ${COL_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])
@@ -55,19 +55,19 @@ checkout() {
# This is unlikely # This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo -e " ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!" echo -e " ${COL_RED}Error: Core Pi-hole repo is missing from system!"
echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}" echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
exit 1; exit 1;
fi fi
if ! is_repo "${webInterfaceDir}" ; then if ! is_repo "${webInterfaceDir}" ; then
echo -e " ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!" echo -e " ${COL_RED}Error: Web Admin repo is missing from system!"
echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}" echo -e " Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}"
exit 1; exit 1;
fi fi
if [[ -z "${1}" ]]; then if [[ -z "${1}" ]]; then
echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC}" echo -e " ${COL_RED}Invalid option${COL_NC}"
echo -e " Try 'pihole checkout --help' for more information." echo -e " Try 'pihole checkout --help' for more information."
exit 1 exit 1
fi fi
@@ -238,7 +238,7 @@ checkout() {
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 -e " ${COL_LIGHT_RED} Error: Unable to complete update, please contact support${COL_NC}" echo -e " ${COL_RED} Error: Unable to complete update, please contact support${COL_NC}"
exit 1 exit 1
fi fi
fi fi

View File

@@ -367,7 +367,7 @@ check_firewalld() {
# test common required service ports # test common required service ports
local firewalld_enabled_services local firewalld_enabled_services
firewalld_enabled_services=$(firewall-cmd --list-services) firewalld_enabled_services=$(firewall-cmd --list-services)
local firewalld_expected_services=("http" "dns" "dhcp" "dhcpv6") local firewalld_expected_services=("http" "https" "dns" "dhcp" "dhcpv6" "ntp")
for i in "${firewalld_expected_services[@]}"; do for i in "${firewalld_expected_services[@]}"; do
if [[ "${firewalld_enabled_services}" =~ ${i} ]]; then if [[ "${firewalld_enabled_services}" =~ ${i} ]]; then
log_write "${TICK} ${COL_GREEN} Allow Service: ${i}${COL_NC}"; log_write "${TICK} ${COL_GREEN} Allow Service: ${i}${COL_NC}";
@@ -388,14 +388,6 @@ check_firewalld() {
else else
log_write "${CROSS} ${COL_RED} Local Interface Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})" log_write "${CROSS} ${COL_RED} Local Interface Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
fi fi
# check FTL custom zone port: 4711
local firewalld_ftl_zone_ports
firewalld_ftl_zone_ports=$(firewall-cmd --zone=ftl --list-ports)
if [[ "${firewalld_ftl_zone_ports}" =~ "4711/tcp" ]]; then
log_write "${TICK} ${COL_GREEN} FTL Port 4711/tcp Detected${COL_NC}";
else
log_write "${CROSS} ${COL_RED} FTL Port 4711/tcp Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
fi
else else
log_write "${CROSS} ${COL_RED}FTL Custom Zone Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})" log_write "${CROSS} ${COL_RED}FTL Custom Zone Not Detected${COL_NC} (${FAQ_HARDWARE_REQUIREMENTS_FIREWALLD})"
fi fi
@@ -497,16 +489,25 @@ ping_gateway() {
ping_ipv4_or_ipv6 "${protocol}" ping_ipv4_or_ipv6 "${protocol}"
# Check if we are using IPv4 or IPv6 # Check if we are using IPv4 or IPv6
# Find the default gateways using IPv4 or IPv6 # Find the default gateways using IPv4 or IPv6
local gateway gateway_addr gateway_iface local gateway gateway_addr gateway_iface default_route
log_write "${INFO} Default IPv${protocol} gateway(s):" log_write "${INFO} Default IPv${protocol} gateway(s):"
while IFS= read -r gateway; do while IFS= read -r default_route; do
log_write " $(cut -d ' ' -f 3 <<< "${gateway}")%$(cut -d ' ' -f 5 <<< "${gateway}")" gateway_addr=$(jq -r '.gateway' <<< "${default_route}")
done < <(ip -"${protocol}" route | grep default) gateway_iface=$(jq -r '.dev' <<< "${default_route}")
log_write " ${gateway_addr}%${gateway_iface}"
done < <(ip -j -"${protocol}" route | jq -c '.[] | select(.dst == "default")')
# Find the first default route
default_route=$(ip -j -"${protocol}" route show default)
if echo "$default_route" | grep 'gateway' | grep -q 'dev'; then
gateway_addr=$(echo "$default_route" | jq -r -c '.[0].gateway')
gateway_iface=$(echo "$default_route" | jq -r -c '.[0].dev')
else
log_write " Unable to determine gateway address for IPv${protocol}"
fi
gateway_addr=$(ip -"${protocol}" route | grep default | cut -d ' ' -f 3 | head -n 1)
gateway_iface=$(ip -"${protocol}" route | grep default | cut -d ' ' -f 5 | head -n 1)
# If there was at least one gateway # If there was at least one gateway
if [ -n "${gateway_addr}" ]; then if [ -n "${gateway_addr}" ]; then
# Append the interface to the gateway address if it is a link-local address # Append the interface to the gateway address if it is a link-local address
@@ -671,7 +672,7 @@ dig_at() {
local record_type="A" local record_type="A"
fi fi
# Find a random blocked url that has not been whitelisted and is not ABP style. # Find a random blocked url that has not been allowlisted and is not ABP style.
# This helps emulate queries to different domains that a user might query # This helps emulate queries to different domains that a user might query
# It will also give extra assurance that Pi-hole is correctly resolving and blocking domains # It will also give extra assurance that Pi-hole is correctly resolving and blocking domains
local random_url local random_url
@@ -777,7 +778,7 @@ process_status(){
: :
else else
# non-Docker system # non-Docker system
if service "${i}" status | grep -E 'is\srunning' &> /dev/null; then if service "${i}" status | grep -q -E 'is\srunning|started'; then
status_of_process="active" status_of_process="active"
else else
status_of_process="inactive" status_of_process="inactive"
@@ -1087,7 +1088,7 @@ show_adlists() {
} }
show_domainlist() { show_domainlist() {
show_db_entries "Domainlist (0/1 = exact white-/blacklist, 2/3 = regex white-/blacklist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "5 4 7 12 100 19 19 50" show_db_entries "Domainlist (0/1 = exact allow-/denylist, 2/3 = regex allow-/denylist)" "SELECT id,CASE type WHEN '0' THEN '0 ' WHEN '1' THEN ' 1 ' WHEN '2' THEN ' 2 ' WHEN '3' THEN ' 3' ELSE type END type,CASE enabled WHEN '0' THEN ' 0' WHEN '1' THEN ' 1' ELSE enabled END enabled,GROUP_CONCAT(domainlist_by_group.group_id) group_ids,domain,datetime(date_added,'unixepoch','localtime') date_added,datetime(date_modified,'unixepoch','localtime') date_modified,comment FROM domainlist LEFT JOIN domainlist_by_group ON domainlist.id = domainlist_by_group.domainlist_id GROUP BY id;" "5 4 7 12 100 19 19 50"
} }
show_clients() { show_clients() {

View File

@@ -17,12 +17,6 @@ utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
# shellcheck source="./advanced/Scripts/utils.sh" # shellcheck source="./advanced/Scripts/utils.sh"
source "${utilsfile}" source "${utilsfile}"
SKIP_INSTALL="true"
# shellcheck source="./automated install/basic-install.sh"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# stop_service() is defined in basic-install.sh
# restart_service() is defined in basic-install.sh
# In case we're running at the same time as a system logrotate, use a # In case we're running at the same time as a system logrotate, use a
# separate logrotate state file to prevent stepping on each other's # separate logrotate state file to prevent stepping on each other's
# toes. # toes.
@@ -92,6 +86,7 @@ if [[ "$*" == *"once"* ]]; then
if [[ "$*" != *"quiet"* ]]; then if [[ "$*" != *"quiet"* ]]; then
echo -ne " ${INFO} Running logrotate ..." echo -ne " ${INFO} Running logrotate ..."
fi fi
mkdir -p "${STATEFILE%/*}"
/usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate /usr/sbin/logrotate --force --state "${STATEFILE}" /etc/pihole/logrotate
else else
# Handle rotation for each log file # Handle rotation for each log file
@@ -110,16 +105,14 @@ else
fi fi
# Stop FTL to make sure it doesn't write to the database while we're deleting data # Stop FTL to make sure it doesn't write to the database while we're deleting data
stop_service pihole-FTL >/dev/null service pihole-FTL stop
# Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history) # Delete most recent 24 hours from FTL's database, leave even older data intact (don't wipe out all history)
deleted=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM query_storage WHERE timestamp >= strftime('%s','now')-86400; select changes() from query_storage limit 1") deleted=$(pihole-FTL sqlite3 -ni "${DBFILE}" "DELETE FROM query_storage WHERE timestamp >= strftime('%s','now')-86400; select changes() from query_storage limit 1")
# Restart FTL # Restart FTL
restart_service pihole-FTL >/dev/null service pihole-FTL restart
if [[ "$*" != *"quiet"* ]]; then if [[ "$*" != *"quiet"* ]]; then
echo -e "${OVER} ${TICK} Deleted ${deleted} queries from long-term query database" echo -e "${OVER} ${TICK} Deleted ${deleted} queries from long-term query database"
fi fi
fi fi

View File

@@ -0,0 +1,84 @@
#!/usr/bin/env bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2019 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Network table flush
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.
coltable="/opt/pihole/COL_TABLE"
if [[ -f ${coltable} ]]; then
# shellcheck source="./advanced/Scripts/COL_TABLE"
source ${coltable}
fi
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole"
utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
# shellcheck source=./advanced/Scripts/utils.sh
source "${utilsfile}"
# Source api functions
# shellcheck source="./advanced/Scripts/api.sh"
. "${PI_HOLE_SCRIPT_DIR}/api.sh"
flushNetwork(){
local output
echo -ne " ${INFO} Flushing network table ..."
local data status error
# Authenticate with FTL
LoginAPI
# send query again
data=$(PostFTLData "action/flush/network" "" "status")
# Separate the status from the data
status=$(printf %s "${data#"${data%???}"}")
data=$(printf %s "${data%???}")
# If there is an .error object in the returned data, display it
local error
error=$(jq --compact-output <<< "${data}" '.error')
if [[ $error != "null" && $error != "" ]]; then
echo -e "${OVER} ${CROSS} Failed to flush the network table:"
echo -e " $(jq <<< "${data}" '.error')"
LogoutAPI
exit 1
elif [[ "${status}" == "200" ]]; then
echo -e "${OVER} ${TICK} Flushed network table"
fi
# Delete session
LogoutAPI
}
flushArp(){
# Flush ARP cache of the host
if ! output=$(ip -s -s neigh flush all 2>&1); then
echo -e "${OVER} ${CROSS} Failed to flush ARP cache"
echo " Output: ${output}"
return 1
fi
}
# Process all options (if present)
while [ "$#" -gt 0 ]; do
case "$1" in
"--arp" ) doARP=true ;;
esac
shift
done
flushNetwork
if [[ "${doARP}" == true ]]; then
echo -ne " ${INFO} Flushing ARP cache"
if flushArp; then
echo -e "${OVER} ${TICK} Flushed ARP cache"
fi
fi

View File

@@ -37,19 +37,16 @@ Options:
} }
GenerateOutput() { GenerateOutput() {
local data gravity_data lists_data num_gravity num_lists search_type_str local counts data num_gravity num_lists search_type_str
local gravity_data_csv lists_data_csv line current_domain url type color local gravity_data_csv lists_data_csv line url type color
data="${1}" data="${1}"
# construct a new json for the list results where each object contains the domain and the related type # Get count of list and gravity matches
lists_data=$(printf %s "${data}" | jq '.search.domains | [.[] | {domain: .domain, type: .type}]') # Use JQ to count number of entries in lists and gravity
# (output is number of list matches then number of gravity matches)
# construct a new json for the gravity results where each object contains the adlist URL and the related domains counts=$(printf %s "${data}" | jq --raw-output '(.search.domains | length), (.search.gravity | group_by(.address,.type) | length)')
gravity_data=$(printf %s "${data}" | jq '.search.gravity | group_by(.address,.type) | map({ address: (.[0].address), type: (.[0].type), domains: [.[] | .domain] })') num_lists=$(echo "$counts" | sed -n '1p')
num_gravity=$(echo "$counts" | sed -n '2p')
# number of objects in each json
num_gravity=$(printf %s "${gravity_data}" | jq length)
num_lists=$(printf %s "${lists_data}" | jq length)
if [ "${partial}" = true ]; then if [ "${partial}" = true ]; then
search_type_str="partially" search_type_str="partially"
@@ -62,7 +59,7 @@ GenerateOutput() {
if [ "${num_lists}" -gt 0 ]; then if [ "${num_lists}" -gt 0 ]; then
# Convert the data to a csv, each line is a "domain,type" string # Convert the data to a csv, each line is a "domain,type" string
# not using jq's @csv here as it quotes each value individually # not using jq's @csv here as it quotes each value individually
lists_data_csv=$(printf %s "${lists_data}" | jq --raw-output '.[] | [.domain, .type] | join(",")') lists_data_csv=$(printf %s "${data}" | jq --raw-output '.search.domains | map([.domain, .type] | join(",")) | join("\n")')
# Generate output for each csv line, separating line in a domain and type substring at the ',' # Generate output for each csv line, separating line in a domain and type substring at the ','
echo "${lists_data_csv}" | while read -r line; do echo "${lists_data_csv}" | while read -r line; do
@@ -71,11 +68,11 @@ GenerateOutput() {
fi fi
# Results from gravity # Results from gravity
printf "%s\n\n" "Found ${num_gravity} adlists ${search_type_str} matching '${COL_BLUE}${domain}${COL_NC}'." printf "%s\n\n" "Found ${num_gravity} lists ${search_type_str} matching '${COL_BLUE}${domain}${COL_NC}'."
if [ "${num_gravity}" -gt 0 ]; then if [ "${num_gravity}" -gt 0 ]; then
# Convert the data to a csv, each line is a "URL,domain,domain,...." string # Convert the data to a csv, each line is a "URL,type,domain,domain,...." string
# not using jq's @csv here as it quotes each value individually # not using jq's @csv here as it quotes each value individually
gravity_data_csv=$(printf %s "${gravity_data}" | jq --raw-output '.[] | [.address, .type, .domains[]] | join(",")') gravity_data_csv=$(printf %s "${data}" | jq --raw-output '.search.gravity | group_by(.address,.type) | map([.[0].address, .[0].type, (.[] | .domain)] | join(",")) | join("\n")')
# Generate line-by-line output for each csv line # Generate line-by-line output for each csv line
echo "${gravity_data_csv}" | while read -r line; do echo "${gravity_data_csv}" | while read -r line; do
@@ -97,15 +94,8 @@ GenerateOutput() {
# cut off type, leaving "domain,domain,...." # cut off type, leaving "domain,domain,...."
line=${line#*,} line=${line#*,}
# print each domain and remove it from the string until nothing is left # Replace commas with newlines and format output
while [ ${#line} -gt 0 ]; do echo "${line}" | sed 's/,/\n/g' | sed "s/^/ - ${COL_GREEN}/" | sed "s/$/${COL_NC}/"
current_domain=${line%%,*}
printf ' - %s\n' "${COL_GREEN}${current_domain}${COL_NC}"
# we need to remove the current_domain and the comma in two steps because
# the last domain won't have a trailing comma and the while loop wouldn't exit
line=${line#"${current_domain}"}
line=${line#,}
done
printf "\n\n" printf "\n\n"
done done
fi fi

View File

@@ -47,7 +47,7 @@ GitCheckUpdateAvail() {
# Fetch latest changes in this repo # Fetch latest changes in this repo
if ! git fetch --quiet origin ; then if ! git fetch --quiet origin ; then
echo -e "\\n ${COL_LIGHT_RED}Error: Unable to update local repository. Contact Pi-hole Support.${COL_NC}" echo -e "\\n ${COL_RED}Error: Unable to update local repository. Contact Pi-hole Support.${COL_NC}"
exit 1 exit 1
fi fi
@@ -76,13 +76,13 @@ GitCheckUpdateAvail() {
if [[ "${#LOCAL}" == 0 ]]; then if [[ "${#LOCAL}" == 0 ]]; then
echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support" echo -e "\\n ${COL_RED}Error: Local revision could not be obtained, please contact Pi-hole Support"
echo -e " Additional debugging output:${COL_NC}" echo -e " Additional debugging output:${COL_NC}"
git status git status
exit 1 exit 1
fi fi
if [[ "${#REMOTE}" == 0 ]]; then if [[ "${#REMOTE}" == 0 ]]; then
echo -e "\\n ${COL_LIGHT_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support" echo -e "\\n ${COL_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support"
echo -e " Additional debugging output:${COL_NC}" echo -e " Additional debugging output:${COL_NC}"
git status git status
exit 1 exit 1
@@ -103,7 +103,7 @@ GitCheckUpdateAvail() {
} }
main() { main() {
local basicError="\\n ${COL_LIGHT_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}" local basicError="\\n ${COL_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}"
local core_update local core_update
local web_update local web_update
local FTL_update local FTL_update
@@ -120,7 +120,7 @@ main() {
# This is unlikely # This is unlikely
if ! is_repo "${PI_HOLE_FILES_DIR}" ; then if ! is_repo "${PI_HOLE_FILES_DIR}" ; then
echo -e "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system!" echo -e "\\n ${COL_RED}Error: Core Pi-hole repo is missing from system!"
echo -e " Please re-run install script from https://pi-hole.net${COL_NC}" echo -e " Please re-run install script from https://pi-hole.net${COL_NC}"
exit 1; exit 1;
fi fi
@@ -132,11 +132,11 @@ main() {
echo -e " ${INFO} Pi-hole Core:\\t${COL_YELLOW}update available${COL_NC}" echo -e " ${INFO} Pi-hole Core:\\t${COL_YELLOW}update available${COL_NC}"
else else
core_update=false core_update=false
echo -e " ${INFO} Pi-hole Core:\\t${COL_LIGHT_GREEN}up to date${COL_NC}" echo -e " ${INFO} Pi-hole Core:\\t${COL_GREEN}up to date${COL_NC}"
fi fi
if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then
echo -e "\\n ${COL_LIGHT_RED}Error: Web Admin repo is missing from system!" echo -e "\\n ${COL_RED}Error: Web Admin repo is missing from system!"
echo -e " Please re-run install script from https://pi-hole.net${COL_NC}" echo -e " Please re-run install script from https://pi-hole.net${COL_NC}"
exit 1; exit 1;
fi fi
@@ -146,7 +146,7 @@ main() {
echo -e " ${INFO} Web Interface:\\t${COL_YELLOW}update available${COL_NC}" echo -e " ${INFO} Web Interface:\\t${COL_YELLOW}update available${COL_NC}"
else else
web_update=false web_update=false
echo -e " ${INFO} Web Interface:\\t${COL_LIGHT_GREEN}up to date${COL_NC}" echo -e " ${INFO} Web Interface:\\t${COL_GREEN}up to date${COL_NC}"
fi fi
local funcOutput local funcOutput
@@ -160,17 +160,18 @@ main() {
else else
case $? in case $? in
1) 1)
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_GREEN}up to date${COL_NC}" echo -e " ${INFO} FTL:\\t\\t${COL_GREEN}up to date${COL_NC}"
;; ;;
2) 2)
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Branch is not available.${COL_NC}\\n\\t\\t\\tUse ${COL_LIGHT_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch." echo -e " ${INFO} FTL:\\t\\t${COL_RED}Branch is not available.${COL_NC}\\n\\t\\t\\tUse ${COL_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch."
exit 1
;; ;;
3) 3)
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Something has gone wrong, cannot reach download server${COL_NC}" echo -e " ${INFO} FTL:\\t\\t${COL_RED}Something has gone wrong, cannot reach download server${COL_NC}"
exit 1 exit 1
;; ;;
*) *)
echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_RED}Something has gone wrong, contact support${COL_NC}" echo -e " ${INFO} FTL:\\t\\t${COL_RED}Something has gone wrong, contact support${COL_NC}"
exit 1 exit 1
esac esac
FTL_update=false FTL_update=false
@@ -187,7 +188,7 @@ main() {
if [[ ! "${ftlBranch}" == "master" && ! "${ftlBranch}" == "development" ]]; then if [[ ! "${ftlBranch}" == "master" && ! "${ftlBranch}" == "development" ]]; then
# Notify user that they are on a custom branch which might mean they they are lost # Notify user that they are on a custom branch which might mean they they are lost
# behind if a branch was merged to development and got abandoned # behind if a branch was merged to development and got abandoned
printf " %b %bWarning:%b You are using FTL from a custom branch (%s) and might be missing future releases.\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}" "${ftlBranch}" printf " %b %bWarning:%b You are using FTL from a custom branch (%s) and might be missing future releases.\\n" "${INFO}" "${COL_RED}" "${COL_NC}" "${ftlBranch}"
fi fi
if [[ "${core_update}" == false && "${web_update}" == false && "${FTL_update}" == false ]]; then if [[ "${core_update}" == false && "${web_update}" == false && "${FTL_update}" == false ]]; then

View File

@@ -73,7 +73,9 @@ getFTLPID() {
# Example getFTLConfigValue dns.piholePTR # Example getFTLConfigValue dns.piholePTR
####################### #######################
getFTLConfigValue(){ getFTLConfigValue(){
pihole-FTL --config -q "${1}" # Pipe to cat to avoid pihole-FTL assuming this is an interactive command
# returning colored output.
pihole-FTL --config -q "${1}" | cat
} }
####################### #######################
@@ -86,9 +88,17 @@ getFTLConfigValue(){
# setFTLConfigValue dns.upstreams '[ "8.8.8.8" , "8.8.4.4" ]' # setFTLConfigValue dns.upstreams '[ "8.8.8.8" , "8.8.4.4" ]'
####################### #######################
setFTLConfigValue(){ setFTLConfigValue(){
pihole-FTL --config "${1}" "${2}" >/dev/null local err
if [ $? -eq 5 ]; then { pihole-FTL --config "${1}" "${2}" >/dev/null; err="$?"; } || true
printf " %s %s set by environment variable. Please unset it to use this function\n" "${CROSS}" "${1}"
exit 5 case $err in
fi 0) ;;
5)
# FTL returns 5 if the value was set by an environment variable and is therefore read-only
printf " %s %s set by environment variable. Please unset it to use this function\n" "${CROSS}" "${1}";
exit 5;;
*)
printf " %s Failed to set %s. Try with sudo power\n" "${CROSS}" "${1}"
exit 1
esac
} }

View File

@@ -66,7 +66,7 @@ CREATE TABLE info
value TEXT NOT NULL value TEXT NOT NULL
); );
INSERT INTO "info" VALUES('version','19'); INSERT INTO "info" VALUES('version','20');
/* This is a flag to indicate if gravity was restored from a backup /* This is a flag to indicate if gravity was restored from a backup
false = not restored, false = not restored,
failed = restoration failed due to no backup failed = restoration failed due to no backup
@@ -111,7 +111,7 @@ CREATE TRIGGER tr_domainlist_update AFTER UPDATE ON domainlist
UPDATE domainlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain; UPDATE domainlist SET date_modified = (cast(strftime('%s', 'now') as int)) WHERE domain = NEW.domain;
END; END;
CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id CREATE VIEW vw_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
@@ -119,7 +119,7 @@ CREATE VIEW vw_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_gr
AND domainlist.type = 0 AND domainlist.type = 0
ORDER BY domainlist.id; ORDER BY domainlist.id;
CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id CREATE VIEW vw_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
@@ -127,7 +127,7 @@ CREATE VIEW vw_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_gr
AND domainlist.type = 1 AND domainlist.type = 1
ORDER BY domainlist.id; ORDER BY domainlist.id;
CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id CREATE VIEW vw_regex_allowlist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id
@@ -135,7 +135,7 @@ CREATE VIEW vw_regex_whitelist AS SELECT domain, domainlist.id AS id, domainlist
AND domainlist.type = 2 AND domainlist.type = 2
ORDER BY domainlist.id; ORDER BY domainlist.id;
CREATE VIEW vw_regex_blacklist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id CREATE VIEW vw_regex_denylist AS SELECT domain, domainlist.id AS id, domainlist_by_group.group_id AS group_id
FROM domainlist FROM domainlist
LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id LEFT JOIN domainlist_by_group ON domainlist_by_group.domainlist_id = domainlist.id
LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id LEFT JOIN "group" ON "group".id = domainlist_by_group.group_id

View File

@@ -0,0 +1,40 @@
#!/sbin/openrc-run
# shellcheck shell=sh disable=SC2034
: "${PI_HOLE_SCRIPT_DIR:=/opt/pihole}"
command="/usr/bin/pihole-FTL"
command_user="pihole:pihole"
supervisor=supervise-daemon
command_args_foreground="-f"
command_background=true
pidfile="/run/${RC_SVCNAME}_openrc.pid"
extra_started_commands="reload"
respawn_max=5
respawn_period=60
capabilities="^CAP_NET_BIND_SERVICE,CAP_NET_RAW,CAP_NET_ADMIN,CAP_SYS_NICE,CAP_IPC_LOCK,CAP_CHOWN,CAP_SYS_TIME"
depend() {
want net
provide dns
}
checkconfig() {
$command -f test
}
start_pre() {
sh "${PI_HOLE_SCRIPT_DIR}/pihole-FTL-prestart.sh"
}
stop_post() {
sh "${PI_HOLE_SCRIPT_DIR}/pihole-FTL-poststop.sh"
}
reload() {
checkconfig || return $?
ebegin "Reloading ${RC_SVCNAME}"
start-stop-daemon --signal HUP --pidfile "${pidfile}"
eend $?
}

View File

@@ -1,51 +0,0 @@
_pihole() {
local cur prev opts opts_checkout opts_debug opts_logging opts_query opts_update opts_version
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
prev2="${COMP_WORDS[COMP_CWORD-2]}"
case "${prev}" in
"pihole")
opts="allow allow-regex allow-wild deny checkout debug disable enable flush help logging query repair regex reloaddns reloadlists status tail uninstall updateGravity updatePihole version wildcard arpflush api"
COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
;;
"allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild")
opts_lists="\not \--delmode \--quiet \--list \--help"
COMPREPLY=( $(compgen -W "${opts_lists}" -- ${cur}) )
;;
"checkout")
opts_checkout="core ftl web master dev"
COMPREPLY=( $(compgen -W "${opts_checkout}" -- ${cur}) )
;;
"debug")
opts_debug="-a"
COMPREPLY=( $(compgen -W "${opts_debug}" -- ${cur}) )
;;
"logging")
opts_logging="on off 'off noflush'"
COMPREPLY=( $(compgen -W "${opts_logging}" -- ${cur}) )
;;
"query")
opts_query="--partial --all"
COMPREPLY=( $(compgen -W "${opts_query}" -- ${cur}) )
;;
"updatePihole"|"-up")
opts_update="--check-only"
COMPREPLY=( $(compgen -W "${opts_update}" -- ${cur}) )
;;
"core"|"admin"|"ftl")
if [[ "$prev2" == "checkout" ]]; then
opts_checkout="master dev"
COMPREPLY=( $(compgen -W "${opts_checkout}" -- ${cur}) )
else
return 1
fi
;;
*)
return 1
;;
esac
return 0
}
complete -F _pihole pihole

View File

@@ -0,0 +1,9 @@
#!/bin/bash
#
# Bash completion script for pihole-FTL
#
# This completion script provides tab completion for pihole-FTL CLI flags and commands.
# It uses the `pihole-FTL --complete` command to generate the completion options.
_complete_FTL() { mapfile -t COMPREPLY < <(pihole-FTL --complete "${COMP_WORDS[@]}"); }
complete -F _complete_FTL pihole-FTL

View File

@@ -0,0 +1,59 @@
#!/bin/bash
#
# Bash completion script for pihole
#
_pihole() {
local cur prev prev2 opts opts_lists opts_checkout opts_debug opts_logging opts_query opts_update opts_networkflush
COMPREPLY=()
cur="${COMP_WORDS[COMP_CWORD]}"
prev="${COMP_WORDS[COMP_CWORD-1]}"
prev2="${COMP_WORDS[COMP_CWORD-2]}"
case "${prev}" in
"pihole")
opts="allow allow-regex allow-wild deny checkout debug disable enable flush help logging query repair regex reloaddns reloadlists setpassword status tail uninstall updateGravity updatePihole version wildcard networkflush api"
mapfile -t COMPREPLY < <(compgen -W "${opts}" -- "${cur}")
;;
"allow"|"deny"|"wildcard"|"regex"|"allow-regex"|"allow-wild")
opts_lists="\not \--delmode \--quiet \--list \--help"
mapfile -t COMPREPLY < <(compgen -W "${opts_lists}" -- "${cur}")
;;
"checkout")
opts_checkout="core ftl web master dev"
mapfile -t COMPREPLY < <(compgen -W "${opts_checkout}" -- "${cur}")
;;
"debug")
opts_debug="-a"
mapfile -t COMPREPLY < <(compgen -W "${opts_debug}" -- "${cur}")
;;
"logging")
opts_logging="on off 'off noflush'"
mapfile -t COMPREPLY < <(compgen -W "${opts_logging}" -- "${cur}")
;;
"query")
opts_query="--partial --all"
mapfile -t COMPREPLY < <(compgen -W "${opts_query}" -- "${cur}")
;;
"updatePihole"|"-up")
opts_update="--check-only"
mapfile -t COMPREPLY < <(compgen -W "${opts_update}" -- "${cur}")
;;
"networkflush")
opts_networkflush="--arp"
mapfile -t COMPREPLY < <(compgen -W "${opts_networkflush}" -- "${cur}")
;;
"core"|"web"|"ftl")
if [[ "$prev2" == "checkout" ]]; then
opts_checkout="master development"
mapfile -t COMPREPLY < <(compgen -W "${opts_checkout}" -- "${cur}")
else
return 1
fi
;;
*)
return 1
;;
esac
return 0
}
complete -F _pihole pihole

View File

@@ -94,8 +94,8 @@ fresh_install=true
adlistFile="/etc/pihole/adlists.list" adlistFile="/etc/pihole/adlists.list"
# Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until this script can run # Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until this script can run
IPV4_ADDRESS=${IPV4_ADDRESS} IPV4_ADDRESS=
IPV6_ADDRESS=${IPV6_ADDRESS} IPV6_ADDRESS=
# Give settings their default values. These may be changed by prompts later in the script. # Give settings their default values. These may be changed by prompts later in the script.
QUERY_LOGGING= QUERY_LOGGING=
PRIVACY_LEVEL= PRIVACY_LEVEL=
@@ -116,11 +116,11 @@ c=70
PIHOLE_META_PACKAGE_CONTROL_APT=$( PIHOLE_META_PACKAGE_CONTROL_APT=$(
cat <<EOM cat <<EOM
Package: pihole-meta Package: pihole-meta
Version: 0.4 Version: 0.5
Maintainer: Pi-hole team <adblock@pi-hole.net> Maintainer: Pi-hole team <adblock@pi-hole.net>
Architecture: all Architecture: all
Description: Pi-hole dependency meta package Description: Pi-hole dependency meta package
Depends: awk,bash-completion,binutils,ca-certificates,cron|cron-daemon,curl,dialog,dnsutils,dns-root-data,git,grep,iproute2,iputils-ping,jq,libcap2,libcap2-bin,lshw,netcat-openbsd,procps,psmisc,sudo,unzip Depends: awk,bash-completion,binutils,ca-certificates,cron|cron-daemon,curl,dialog,dnsutils,dns-root-data,git,grep,iproute2,iputils-ping,jq,libcap2,libcap2-bin,lshw,procps,psmisc,sudo,unzip
Section: contrib/metapackages Section: contrib/metapackages
Priority: optional Priority: optional
EOM EOM
@@ -130,12 +130,12 @@ EOM
PIHOLE_META_PACKAGE_CONTROL_RPM=$( PIHOLE_META_PACKAGE_CONTROL_RPM=$(
cat <<EOM cat <<EOM
Name: pihole-meta Name: pihole-meta
Version: 0.2 Version: 0.3
Release: 1 Release: 1
License: EUPL License: EUPL
BuildArch: noarch BuildArch: noarch
Summary: Pi-hole dependency meta package Summary: Pi-hole dependency meta package
Requires: bash-completion,bind-utils,binutils,ca-certificates,chkconfig,cronie,curl,dialog,findutils,gawk,git,grep,iproute,jq,libcap,lshw,nmap-ncat,procps-ng,psmisc,sudo,unzip Requires: bash-completion,bind-utils,binutils,ca-certificates,chkconfig,cronie,curl,dialog,findutils,gawk,git,grep,iproute,jq,libcap,lshw,procps-ng,psmisc,sudo,unzip
%description %description
Pi-hole dependency meta package Pi-hole dependency meta package
%prep %prep
@@ -143,6 +143,9 @@ Pi-hole dependency meta package
%files %files
%install %install
%changelog %changelog
* Mon Jul 14 2025 Pi-hole Team - 0.3
- Remove nmap-ncat from the list of dependencies
* Wed May 28 2025 Pi-hole Team - 0.2 * Wed May 28 2025 Pi-hole Team - 0.2
- Add gawk to the list of dependencies - Add gawk to the list of dependencies
@@ -151,6 +154,36 @@ Pi-hole dependency meta package
EOM EOM
) )
# List of required packages on APK based systems
PIHOLE_META_VERSION_APK=0.1
PIHOLE_META_DEPS_APK=(
bash
bash-completion
bind-tools
binutils
coreutils
cronie
curl
dialog
git
grep
iproute2-minimal # piholeARPTable.sh
iproute2-ss # piholeDebug.sh
jq
libcap
logrotate
lscpu # piholeDebug.sh
lshw # piholeDebug.sh
ncurses
procps-ng
psmisc
shadow
sudo
tzdata
unzip
wget
)
######## Undocumented Flags. Shhh ######## ######## Undocumented Flags. Shhh ########
# These are undocumented flags; some of which we can use when repairing an installation # These are undocumented flags; some of which we can use when repairing an installation
# The runUnattended flag is one example of this # The runUnattended flag is one example of this
@@ -158,7 +191,7 @@ repair=false
runUnattended=false runUnattended=false
# Check arguments for the undocumented flags # Check arguments for the undocumented flags
for var in "$@"; do for var in "$@"; do
case "$var" in case "${var}" in
"--repair") repair=true ;; "--repair") repair=true ;;
"--unattended") runUnattended=true ;; "--unattended") runUnattended=true ;;
esac esac
@@ -173,13 +206,11 @@ if [[ -f "${coltable}" ]]; then
else else
# Set these values so the installer can still run in color # Set these values so the installer can still run in color
COL_NC='\e[0m' # No Color COL_NC='\e[0m' # No Color
COL_LIGHT_GREEN='\e[1;32m' COL_GREEN='\e[1;32m'
COL_LIGHT_RED='\e[1;31m' COL_RED='\e[1;31m'
TICK="[${COL_LIGHT_GREEN}${COL_NC}]" TICK="[${COL_GREEN}${COL_NC}]"
CROSS="[${COL_LIGHT_RED}${COL_NC}]" CROSS="[${COL_RED}${COL_NC}]"
INFO="[i]" INFO="[i]"
# shellcheck disable=SC2034
DONE="${COL_LIGHT_GREEN} done!${COL_NC}"
OVER="\\r\\033[K" OVER="\\r\\033[K"
fi fi
@@ -187,13 +218,13 @@ fi
# This lets users know that it is a Pi-hole, LLC product # This lets users know that it is a Pi-hole, LLC product
show_ascii_berry() { show_ascii_berry() {
echo -e " echo -e "
${COL_LIGHT_GREEN}.;;,. ${COL_GREEN}.;;,.
.ccccc:,. .ccccc:,.
:cccclll:. ..,, :cccclll:. ..,,
:ccccclll. ;ooodc :ccccclll. ;ooodc
'ccll:;ll .oooodc 'ccll:;ll .oooodc
.;cll.;;looo:. .;cll.;;looo:.
${COL_LIGHT_RED}.. ','. ${COL_RED}.. ','.
.',,,,,,'. .',,,,,,'.
.',,,,,,,,,,. .',,,,,,,,,,.
.',,,,,,,,,,,,.... .',,,,,,,,,,,,....
@@ -215,7 +246,7 @@ abort() {
# remove any leftover build directory that may exist # remove any leftover build directory that may exist
rm -rf /tmp/pihole-meta_* rm -rf /tmp/pihole-meta_*
echo -e "\\n\\n ${COL_LIGHT_RED}Installation was interrupted${COL_NC}\\n" echo -e "\\n\\n ${COL_RED}Installation was interrupted${COL_NC}\\n"
echo -e "Pi-hole's dependencies might be already installed. If you want to remove them you can try to\\n" echo -e "Pi-hole's dependencies might be already installed. If you want to remove them you can try to\\n"
echo -e "a) run 'pihole uninstall' \\n" echo -e "a) run 'pihole uninstall' \\n"
echo -e "b) Remove the meta-package 'pihole-meta' manually \\n" echo -e "b) Remove the meta-package 'pihole-meta' manually \\n"
@@ -231,13 +262,11 @@ is_command() {
command -v "${check_command}" >/dev/null 2>&1 command -v "${check_command}" >/dev/null 2>&1
} }
is_pid1() { check_fresh_install() {
# Checks to see if the given command runs as PID 1 # in case of an update (can be a v5 -> v6 or v6 -> v6 update) or repair
local is_pid1="$1" if [[ -f "${PI_HOLE_V6_CONFIG}" ]] || [[ -f "/etc/pihole/setupVars.conf" ]]; then
fresh_install=false
# select PID 1, format output to show only CMD column without header fi
# quietly grep for a match on the function passed parameter
ps --pid 1 --format comm= | grep -q "${is_pid1}"
} }
# Compatibility # Compatibility
@@ -256,8 +285,6 @@ package_manager_detect() {
PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true"
# The command we will use to remove packages (used in the uninstaller) # The command we will use to remove packages (used in the uninstaller)
PKG_REMOVE="${PKG_MANAGER} -y remove --purge" PKG_REMOVE="${PKG_MANAGER} -y remove --purge"
# Update package cache
update_package_cache || exit 1
# If apt-get is not found, check for rpm. # If apt-get is not found, check for rpm.
elif is_command rpm; then elif is_command rpm; then
@@ -274,7 +301,15 @@ package_manager_detect() {
PKG_COUNT="${PKG_MANAGER} check-update | grep -E '(.i686|.x86|.noarch|.arm|.src|.riscv64)' | wc -l || true" PKG_COUNT="${PKG_MANAGER} check-update | grep -E '(.i686|.x86|.noarch|.arm|.src|.riscv64)' | wc -l || true"
# The command we will use to remove packages (used in the uninstaller) # The command we will use to remove packages (used in the uninstaller)
PKG_REMOVE="${PKG_MANAGER} remove -y" PKG_REMOVE="${PKG_MANAGER} remove -y"
# If neither apt-get or yum/dnf package managers were found
# If neither apt-get or yum/dnf package managers were found, check for apk.
elif is_command apk; then
PKG_MANAGER="apk"
UPDATE_PKG_CACHE="${PKG_MANAGER} update"
PKG_INSTALL="${PKG_MANAGER} add"
PKG_COUNT="${PKG_MANAGER} list --upgradable -q | wc -l"
PKG_REMOVE="${PKG_MANAGER} del"
else else
# we cannot install required packages # we cannot install required packages
printf " %b No supported package manager found\\n" "${CROSS}" printf " %b No supported package manager found\\n" "${CROSS}"
@@ -285,13 +320,20 @@ package_manager_detect() {
build_dependency_package(){ build_dependency_package(){
# This function will build a package that contains all the dependencies needed for Pi-hole # This function will build a package that contains all the dependencies needed for Pi-hole
if is_command apk ; then
local str="APK based system detected. Dependencies will be installed using a virtual package named pihole-meta"
printf " %b %s...\\n" "${INFO}" "${str}"
return 0
fi
# remove any leftover build directory that may exist # remove any leftover build directory that may exist
rm -rf /tmp/pihole-meta_* rm -rf /tmp/pihole-meta_*
# Create a fresh build directory with random name # Create a fresh build directory with random name
# Busybox Compat: `mktemp` long flags unsupported
# -d flag is short form of --directory
local tempdir local tempdir
tempdir="$(mktemp --directory /tmp/pihole-meta_XXXXX)" tempdir="$(mktemp -d /tmp/pihole-meta_XXXXX)"
chmod 0755 "${tempdir}" chmod 0755 "${tempdir}"
if is_command apt-get; then if is_command apt-get; then
@@ -317,7 +359,7 @@ build_dependency_package(){
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf "%b Error: Building pihole-meta.deb failed. %b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf "%b Error: Building pihole-meta.deb failed. %b\\n" "${COL_RED}" "${COL_NC}"
return 1 return 1
fi fi
@@ -350,7 +392,7 @@ build_dependency_package(){
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf "%b Error: Building pihole-meta.rpm failed. %b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf "%b Error: Building pihole-meta.rpm failed. %b\\n" "${COL_RED}" "${COL_NC}"
return 1 return 1
fi fi
@@ -492,7 +534,7 @@ getGitFiles() {
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Update the repo, returning an error message on failure # Update the repo, returning an error message on failure
update_repo "${directory}" || { update_repo "${directory}" || {
printf "\\n %b: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf "\\n %b: Could not update local repository. Contact support.%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
} }
# If it's not a .git repo, # If it's not a .git repo,
@@ -501,7 +543,7 @@ getGitFiles() {
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
# Attempt to make the repository, showing an error on failure # Attempt to make the repository, showing an error on failure
make_repo "${directory}" "${remoteRepo}" || { make_repo "${directory}" "${remoteRepo}" || {
printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf "\\n %bError: Could not update local repository. Contact support.%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
} }
fi fi
@@ -579,14 +621,17 @@ Do you wish to continue with an IPv6-only installation?\\n\\n" \
;; ;;
esac esac
DNS_SERVERS="$DNS_SERVERS_IPV6_ONLY" DNS_SERVERS="${DNS_SERVERS_IPV6_ONLY}"
printf " %b Proceeding with IPv6 only installation.\\n" "${INFO}" printf " %b Proceeding with IPv6 only installation.\\n" "${INFO}"
} }
# Get available interfaces that are UP # Get available interfaces that are UP
get_available_interfaces() { get_available_interfaces() {
# There may be more than one so it's all stored in a variable # There may be more than one so it's all stored in a variable
availableInterfaces=$(ip --oneline link show up | awk '{print $2}' | grep -v "^lo" | cut -d':' -f1 | cut -d'@' -f1) # The ip command list all interfaces that are in the up state
# The awk command filters out any interfaces that have the LOOPBACK flag set
# while using the characters ": " or "@" as a field separator for awk
availableInterfaces=$(ip --oneline link show up | awk -F ': |@' '!/<.*LOOPBACK.*>/ {print $2}')
} }
# A function for displaying the dialogs the user sees when first running the installer # A function for displaying the dialogs the user sees when first running the installer
@@ -652,7 +697,7 @@ chooseInterface() {
PIHOLE_INTERFACE=$(dialog --no-shadow --keep-tite --output-fd 1 \ PIHOLE_INTERFACE=$(dialog --no-shadow --keep-tite --output-fd 1 \
--cancel-label "Exit" --ok-label "Select" \ --cancel-label "Exit" --ok-label "Select" \
--radiolist "Choose An Interface (press space to toggle selection)" \ --radiolist "Choose An Interface (press space to toggle selection)" \
${r} ${c} "${interfaceCount}" ${interfacesList}) ${r} ${c} "${interfaceCount}" "${interfacesList}")
result=$? result=$?
case ${result} in case ${result} in
@@ -674,9 +719,9 @@ testIPv6() {
# first will contain fda2 (ULA) # first will contain fda2 (ULA)
printf -v first "%s" "${1%%:*}" printf -v first "%s" "${1%%:*}"
# value1 will contain 253 which is the decimal value corresponding to 0xFD # value1 will contain 253 which is the decimal value corresponding to 0xFD
value1=$(((0x$first) / 256)) value1=$(((0x${first}) / 256))
# value2 will contain 162 which is the decimal value corresponding to 0xA2 # value2 will contain 162 which is the decimal value corresponding to 0xA2
value2=$(((0x$first) % 256)) value2=$(((0x${first}) % 256))
# the ULA test is testing for fc00::/7 according to RFC 4193 # the ULA test is testing for fc00::/7 according to RFC 4193
if (((value1 & 254) == 252)); then if (((value1 & 254) == 252)); then
# echoing result to calling function as return value # echoing result to calling function as return value
@@ -701,7 +746,7 @@ find_IPv6_information() {
# For each address in the array above, determine the type of IPv6 address it is # For each address in the array above, determine the type of IPv6 address it is
for i in "${IPV6_ADDRESSES[@]}"; do for i in "${IPV6_ADDRESSES[@]}"; do
# Check if it's ULA, GUA, or LL by using the function created earlier # Check if it's ULA, GUA, or LL by using the function created earlier
result=$(testIPv6 "$i") result=$(testIPv6 "${i}")
# If it's a ULA address, use it and store it as a global variable # If it's a ULA address, use it and store it as a global variable
[[ "${result}" == "ULA" ]] && ULA_ADDRESS="${i%/*}" [[ "${result}" == "ULA" ]] && ULA_ADDRESS="${i%/*}"
# If it's a GUA address, use it and store it as a global variable # If it's a GUA address, use it and store it as a global variable
@@ -736,7 +781,7 @@ collect_v4andv6_information() {
printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}" printf " %b IPv4 address: %s\\n" "${INFO}" "${IPV4_ADDRESS}"
find_IPv6_information find_IPv6_information
printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}" printf " %b IPv6 address: %s\\n" "${INFO}" "${IPV6_ADDRESS}"
if [ "$IPV4_ADDRESS" == "" ] && [ "$IPV6_ADDRESS" != "" ]; then if [ "${IPV4_ADDRESS}" == "" ] && [ "${IPV6_ADDRESS}" != "" ]; then
confirm_ipv6_only confirm_ipv6_only
fi fi
} }
@@ -756,7 +801,7 @@ valid_ip() {
local regex="^${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}${portelem}$" local regex="^${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}\\.${ipv4elem}${portelem}$"
# Evaluate the regex, and return the result # Evaluate the regex, and return the result
[[ $ip =~ ${regex} ]] [[ ${ip} =~ ${regex} ]]
stat=$? stat=$?
return "${stat}" return "${stat}"
@@ -791,7 +836,7 @@ setDNS() {
DNSChooseOptions=() DNSChooseOptions=()
local DNSServerCount=0 local DNSServerCount=0
# Save the old Internal Field Separator in a variable, # Save the old Internal Field Separator in a variable,
OIFS=$IFS OIFS=${IFS}
# and set the new one to newline # and set the new one to newline
IFS=$'\n' IFS=$'\n'
# Put the DNS Servers into an array # Put the DNS Servers into an array
@@ -816,7 +861,7 @@ setDNS() {
result=$? result=$?
case ${result} in case ${result} in
"${DIALOG_CANCEL}" | "${DIALOG_ESC}") "${DIALOG_CANCEL}" | "${DIALOG_ESC}")
printf " %b Cancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Cancel was selected, exiting installer%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
;; ;;
esac esac
@@ -853,13 +898,13 @@ If you want to specify a port other than 53, separate it with a hash.\
result=$? result=$?
case ${result} in case ${result} in
"${DIALOG_CANCEL}" | "${DIALOG_ESC}") "${DIALOG_CANCEL}" | "${DIALOG_ESC}")
printf " %b Cancel was selected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Cancel was selected, exiting installer%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
;; ;;
esac esac
# Clean user input and replace whitespace with comma. # Clean user input and replace whitespace with comma.
piholeDNS=$(sed 's/[, \t]\+/,/g' <<<"${piholeDNS}") piholeDNS="${piholeDNS//[[:blank:]]/,}"
# Separate the user input into the two DNS values (separated by a comma) # Separate the user input into the two DNS values (separated by a comma)
printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}" printf -v PIHOLE_DNS_1 "%s" "${piholeDNS%%,*}"
@@ -907,7 +952,7 @@ If you want to specify a port other than 53, separate it with a hash.\
DNSSettingsCorrect=False DNSSettingsCorrect=False
;; ;;
"${DIALOG_ESC}") "${DIALOG_ESC}")
printf " %b Escape pressed, exiting installer at DNS Settings%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Escape pressed, exiting installer at DNS Settings%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
;; ;;
esac esac
@@ -915,7 +960,7 @@ If you want to specify a port other than 53, separate it with a hash.\
done done
else else
# Save the old Internal Field Separator in a variable, # Save the old Internal Field Separator in a variable,
OIFS=$IFS OIFS=${IFS}
# and set the new one to newline # and set the new one to newline
IFS=$'\n' IFS=$'\n'
for DNSServer in ${DNS_SERVERS}; do for DNSServer in ${DNS_SERVERS}; do
@@ -958,7 +1003,7 @@ setLogging() {
;; ;;
"${DIALOG_ESC}") "${DIALOG_ESC}")
# User pressed <ESC> # User pressed <ESC>
printf " %b Escape pressed, exiting installer at Query Logging choice.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Escape pressed, exiting installer at Query Logging choice.%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
;; ;;
esac esac
@@ -983,7 +1028,7 @@ setPrivacyLevel() {
printf " %b Using privacy level: %s\\n" "${INFO}" "${PRIVACY_LEVEL}" printf " %b Using privacy level: %s\\n" "${INFO}" "${PRIVACY_LEVEL}"
;; ;;
"${DIALOG_CANCEL}" | "${DIALOG_ESC}") "${DIALOG_CANCEL}" | "${DIALOG_ESC}")
printf " %b Cancelled privacy level selection.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Cancelled privacy level selection.%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
;; ;;
esac esac
@@ -1017,7 +1062,7 @@ chooseBlocklists() {
;; ;;
"${DIALOG_ESC}") "${DIALOG_ESC}")
# User pressed <ESC> # User pressed <ESC>
printf " %b Escape pressed, exiting installer at blocklist choice.%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b Escape pressed, exiting installer at blocklist choice.%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
;; ;;
esac esac
@@ -1137,13 +1182,14 @@ installScripts() {
install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./automated\ install/uninstall.sh install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./automated\ install/uninstall.sh
install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./advanced/Scripts/COL_TABLE install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./advanced/Scripts/COL_TABLE
install -o "${USER}" -Dm755 -t "${PI_HOLE_BIN_DIR}" pihole install -o "${USER}" -Dm755 -t "${PI_HOLE_BIN_DIR}" pihole
install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole install -Dm644 ./advanced/bash-completion/pihole.bash /etc/bash_completion.d/pihole
install -Dm644 ./advanced/bash-completion/pihole-ftl.bash /etc/bash_completion.d/pihole-FTL
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
# Otherwise, show an error and exit # Otherwise, show an error and exit
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf "\\t\\t%bError: Local repo %s not found, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}" printf "\\t\\t%bError: Local repo %s not found, exiting installer%b\\n" "${COL_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"
return 1 return 1
fi fi
} }
@@ -1158,13 +1204,13 @@ installConfigs() {
# Install empty custom.list file if it does not exist # Install empty custom.list file if it does not exist
if [[ ! -r "${PI_HOLE_CONFIG_DIR}/hosts/custom.list" ]]; then if [[ ! -r "${PI_HOLE_CONFIG_DIR}/hosts/custom.list" ]]; then
if ! install -D -T -o pihole -g pihole -m 660 /dev/null "${PI_HOLE_CONFIG_DIR}/hosts/custom.list" &>/dev/null; then if ! install -D -T -o pihole -g pihole -m 660 /dev/null "${PI_HOLE_CONFIG_DIR}/hosts/custom.list" &>/dev/null; then
printf " %b Error: Unable to initialize configuration file %s/custom.list\\n" "${COL_LIGHT_RED}" "${PI_HOLE_CONFIG_DIR}/hosts" printf " %b Error: Unable to initialize configuration file %s/custom.list\\n" "${COL_RED}" "${PI_HOLE_CONFIG_DIR}/hosts"
return 1 return 1
fi fi
fi fi
# Install pihole-FTL systemd or init.d service, based on whether systemd is the init system or not # Install pihole-FTL systemd or init.d service, based on whether systemd is the init system or not
if is_pid1 systemd; then if ps -p 1 -o comm= | grep -q systemd; then
install -T -m 0644 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.systemd" '/etc/systemd/system/pihole-FTL.service' install -T -m 0644 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.systemd" '/etc/systemd/system/pihole-FTL.service'
# Remove init.d service if present # Remove init.d service if present
@@ -1176,7 +1222,12 @@ installConfigs() {
# Load final service # Load final service
systemctl daemon-reload systemctl daemon-reload
else else
install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.service" '/etc/init.d/pihole-FTL' local INIT="service"
if is_command openrc; then
INIT="openrc"
fi
install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.${INIT}" '/etc/init.d/pihole-FTL'
fi fi
install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL-prestart.sh" "${PI_HOLE_INSTALL_DIR}/pihole-FTL-prestart.sh" install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL-prestart.sh" "${PI_HOLE_INSTALL_DIR}/pihole-FTL-prestart.sh"
install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL-poststop.sh" "${PI_HOLE_INSTALL_DIR}/pihole-FTL-poststop.sh" install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL-poststop.sh" "${PI_HOLE_INSTALL_DIR}/pihole-FTL-poststop.sh"
@@ -1232,12 +1283,9 @@ stop_service() {
# Can softfail, as process may not be installed when this is called # Can softfail, as process may not be installed when this is called
local str="Stopping ${1} service" local str="Stopping ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemd is PID 1, if is_command systemctl; then
if is_pid1 systemd; then
# use that to restart the service
systemctl -q stop "${1}" || true systemctl -q stop "${1}" || true
else else
# Otherwise, fall back to the service command
service "${1}" stop >/dev/null || true service "${1}" stop >/dev/null || true
fi fi
printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${TICK}" "${str}"
@@ -1248,8 +1296,8 @@ restart_service() {
# Local, named variables # Local, named variables
local str="Restarting ${1} service" local str="Restarting ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemd is PID 1, # If systemctl exists,
if is_pid1 systemd; then if is_command systemctl; then
# use that to restart the service # use that to restart the service
systemctl -q restart "${1}" systemctl -q restart "${1}"
else else
@@ -1264,10 +1312,12 @@ enable_service() {
# Local, named variables # Local, named variables
local str="Enabling ${1} service to start on reboot" local str="Enabling ${1} service to start on reboot"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemd is PID1, # If systemctl exists,
if is_pid1 systemd; then if is_command systemctl; then
# use that to enable the service # use that to enable the service
systemctl -q enable "${1}" systemctl -q enable "${1}"
elif is_command openrc; then
rc-update add "${1}" "${2:-default}" &> /dev/null
else else
# Otherwise, use update-rc.d to accomplish this # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" defaults >/dev/null update-rc.d "${1}" defaults >/dev/null
@@ -1280,10 +1330,13 @@ disable_service() {
# Local, named variables # Local, named variables
local str="Disabling ${1} service" local str="Disabling ${1} service"
printf " %b %s..." "${INFO}" "${str}" printf " %b %s..." "${INFO}" "${str}"
# If systemd is PID1, # If systemctl exists,
if is_pid1 systemd; then if is_command systemctl; then
# use that to disable the service # use that to disable the service
systemctl -q disable "${1}" systemctl -q disable --now "${1}"
elif is_command openrc; then
rc-update del "${1}" "${2:-default}" &> /dev/null
else else
# Otherwise, use update-rc.d to accomplish this # Otherwise, use update-rc.d to accomplish this
update-rc.d "${1}" disable >/dev/null update-rc.d "${1}" disable >/dev/null
@@ -1292,10 +1345,12 @@ disable_service() {
} }
check_service_active() { check_service_active() {
# If systemd is PID1, # If systemctl exists,
if is_pid1 systemd; then if is_command systemctl; then
# use that to check the status of the service # use that to check the status of the service
systemctl -q is-enabled "${1}" 2>/dev/null systemctl -q is-enabled "${1}" 2>/dev/null
elif is_command openrc; then
rc-status default boot | grep -q "${1}"
else else
# Otherwise, fall back to service command # Otherwise, fall back to service command
service "${1}" status &>/dev/null service "${1}" status &>/dev/null
@@ -1340,7 +1395,7 @@ update_package_cache() {
UPDATE_PKG_CACHE="apt update" UPDATE_PKG_CACHE="apt update"
fi fi
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b Error: Unable to update package cache. Please try \"%s\"%b\\n" "${COL_LIGHT_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}" printf " %b Error: Unable to update package cache. Please try \"%s\"%b\\n" "${COL_RED}" "sudo ${UPDATE_PKG_CACHE}" "${COL_NC}"
return 1 return 1
fi fi
} }
@@ -1358,7 +1413,7 @@ notify_package_updates_available() {
printf "%b %b %s... up to date!\\n\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s... up to date!\\n\\n" "${OVER}" "${TICK}" "${str}"
else else
printf "%b %b %s... %s updates available\\n" "${OVER}" "${TICK}" "${str}" "${updatesToInstall}" printf "%b %b %s... %s updates available\\n" "${OVER}" "${TICK}" "${str}" "${updatesToInstall}"
printf " %b %bIt is recommended to update your OS after installing the Pi-hole!%b\\n\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${COL_NC}" printf " %b %bIt is recommended to update your OS after installing the Pi-hole!%b\\n\\n" "${INFO}" "${COL_GREEN}" "${COL_NC}"
fi fi
} }
@@ -1375,11 +1430,11 @@ install_dependent_packages() {
rm /tmp/pihole-meta.deb rm /tmp/pihole-meta.deb
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b Error: Unable to install Pi-hole dependency package.\\n" "${COL_LIGHT_RED}" printf " %b Error: Unable to install Pi-hole dependency package.\\n" "${COL_RED}"
return 1 return 1
fi fi
else else
printf " %b Error: Unable to find Pi-hole dependency package.\\n" "${COL_LIGHT_RED}" printf " %b Error: Unable to find Pi-hole dependency package.\\n" "${COL_RED}"
return 1 return 1
fi fi
# Install Fedora/CentOS packages # Install Fedora/CentOS packages
@@ -1390,15 +1445,34 @@ install_dependent_packages() {
rm /tmp/pihole-meta.rpm rm /tmp/pihole-meta.rpm
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b Error: Unable to install Pi-hole dependency package.\\n" "${COL_LIGHT_RED}" printf " %b Error: Unable to install Pi-hole dependency package.\\n" "${COL_RED}"
return 1 return 1
fi fi
else else
printf " %b Error: Unable to find Pi-hole dependency package.\\n" "${COL_LIGHT_RED}" printf " %b Error: Unable to find Pi-hole dependency package.\\n" "${COL_RED}"
return 1 return 1
fi fi
# Install Alpine packages
elif is_command apk; then
local repo_str="Ensuring alpine 'community' repo is enabled."
printf "%b %b %s" "${OVER}" "${INFO}" "${repo_str}"
# If neither apt-get or yum/dnf package managers were found local pattern='^\s*#(.*/community/?)\s*$'
sed -Ei "s:${pattern}:\1:" /etc/apk/repositories
if grep -Eq "${pattern}" /etc/apk/repositories; then
# Repo still commented out = Failure
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${repo_str}"
else
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${repo_str}"
fi
printf " %b %s..." "${INFO}" "${str}"
if { ${PKG_INSTALL} -q -t "pihole-meta=${PIHOLE_META_VERSION_APK}" "${PIHOLE_META_DEPS_APK[@]}" &>/dev/null; }; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b Error: Unable to install Pi-hole dependency package.\\n" "${COL_RED}"
return 1
fi
else else
# we cannot install the dependency package # we cannot install the dependency package
printf " %b No supported package manager found\\n" "${CROSS}" printf " %b No supported package manager found\\n" "${CROSS}"
@@ -1423,6 +1497,15 @@ installCron() {
# Randomize update checker time # Randomize update checker time
sed -i "s/59 17/$((1 + RANDOM % 58)) $((12 + RANDOM % 8))/" /etc/cron.d/pihole sed -i "s/59 17/$((1 + RANDOM % 58)) $((12 + RANDOM % 8))/" /etc/cron.d/pihole
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Switch off of busybox cron on alpine
if is_command openrc; then
printf " %b Switching from busybox crond to cronie...\\n" "${INFO}"
stop_service crond
disable_service crond
enable_service cronie
restart_service cronie
fi
} }
# Gravity is a very important script as it aggregates all of the domains into a single HOSTS formatted list, # Gravity is a very important script as it aggregates all of the domains into a single HOSTS formatted list,
@@ -1472,7 +1555,7 @@ create_pihole_user() {
# then create and add her to the pihole group # then create and add her to the pihole group
local str="Creating user 'pihole'" local str="Creating user 'pihole'"
printf "%b %b %s..." "${OVER}" "${INFO}" "${str}" printf "%b %b %s..." "${OVER}" "${INFO}" "${str}"
if useradd -r --no-user-group -g pihole -s /usr/sbin/nologin pihole; then if useradd -r --no-user-group -g pihole -s "$(command -v nologin)" pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
@@ -1487,7 +1570,7 @@ create_pihole_user() {
# create and add pihole user to the pihole group # create and add pihole user to the pihole group
local str="Creating user 'pihole'" local str="Creating user 'pihole'"
printf "%b %b %s..." "${OVER}" "${INFO}" "${str}" printf "%b %b %s..." "${OVER}" "${INFO}" "${str}"
if useradd -r --no-user-group -g pihole -s /usr/sbin/nologin pihole; then if useradd -r --no-user-group -g pihole -s "$(command -v nologin)" pihole; then
printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
else else
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
@@ -1623,13 +1706,13 @@ checkSelinux() {
if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then if [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -z "${PIHOLE_SELINUX}" ]]; then
printf " Pi-hole does not provide an SELinux policy as the required changes modify the security of your system.\\n" printf " Pi-hole does not provide an SELinux policy as the required changes modify the security of your system.\\n"
printf " Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment.\\n" printf " Please refer to https://wiki.centos.org/HowTos/SELinux if SELinux is required for your deployment.\\n"
printf " This check can be skipped by setting the environment variable %bPIHOLE_SELINUX%b to %btrue%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" "${COL_LIGHT_RED}" "${COL_NC}" printf " This check can be skipped by setting the environment variable %bPIHOLE_SELINUX%b to %btrue%b\\n" "${COL_RED}" "${COL_NC}" "${COL_RED}" "${COL_NC}"
printf " e.g: export PIHOLE_SELINUX=true\\n" printf " e.g: export PIHOLE_SELINUX=true\\n"
printf " By setting this variable to true you acknowledge there may be issues with Pi-hole during or after the install\\n" printf " By setting this variable to true you acknowledge there may be issues with Pi-hole during or after the install\\n"
printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_LIGHT_RED}" "${COL_NC}" printf "\\n %bSELinux Enforcing detected, exiting installer%b\\n" "${COL_RED}" "${COL_NC}"
exit 1 exit 1
elif [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -n "${PIHOLE_SELINUX}" ]]; then elif [[ "${SELINUX_ENFORCING}" -eq 1 ]] && [[ -n "${PIHOLE_SELINUX}" ]]; then
printf " %b %bSELinux Enforcing detected%b. PIHOLE_SELINUX env variable set - installer will continue\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b %bSELinux Enforcing detected%b. PIHOLE_SELINUX env variable set - installer will continue\\n" "${INFO}" "${COL_RED}" "${COL_NC}"
fi fi
} }
@@ -1639,9 +1722,9 @@ check_download_exists() {
status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1) status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1)
# Check the status code # Check the status code
if grep -q "200" <<<"$status"; then if grep -q "200" <<<"${status}"; then
return 0 return 0
elif grep -q "404" <<<"$status"; then elif grep -q "404" <<<"${status}"; then
return 1 return 1
fi fi
@@ -1674,7 +1757,7 @@ get_available_branches() {
# Get reachable remote branches, but store STDERR as STDOUT variable # Get reachable remote branches, but store STDERR as STDOUT variable
output=$({ git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1) output=$({ git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1)
# echo status for calling function to capture # echo status for calling function to capture
echo "$output" echo "${output}"
return return
} }
@@ -1707,9 +1790,9 @@ checkout_pull_branch() {
oldbranch="$(git symbolic-ref HEAD)" oldbranch="$(git symbolic-ref HEAD)"
str="Switching to branch: '${branch}' from '${oldbranch}'" str="Switching to branch: '${branch}' from '${oldbranch}'"
printf " %b %s" "${INFO}" "$str" printf " %b %s" "${INFO}" "${str}"
git checkout "${branch}" --quiet || return 1 git checkout "${branch}" --quiet || return 1
printf "%b %b %s\\n" "${OVER}" "${TICK}" "$str" printf "%b %b %s\\n" "${OVER}" "${TICK}" "${str}"
# Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git) # Data in the repositories is public anyway so we can make it readable by everyone (+r to keep executable permission if already set by git)
chmod -R a+rX "${directory}" chmod -R a+rX "${directory}"
@@ -1727,13 +1810,13 @@ clone_or_reset_repos() {
# Reset the Core repo # Reset the Core repo
resetRepo ${PI_HOLE_LOCAL_REPO} || resetRepo ${PI_HOLE_LOCAL_REPO} ||
{ {
printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}" printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_RED}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"
exit 1 exit 1
} }
# Reset the Web repo # Reset the Web repo
resetRepo ${webInterfaceDir} || resetRepo ${webInterfaceDir} ||
{ {
printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceDir}" "${COL_NC}" printf " %b Unable to reset %s, exiting installer%b\\n" "${COL_RED}" "${webInterfaceDir}" "${COL_NC}"
exit 1 exit 1
} }
# Otherwise, a fresh installation is happening # Otherwise, a fresh installation is happening
@@ -1741,13 +1824,13 @@ clone_or_reset_repos() {
# so get git files for Core # so get git files for Core
getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} ||
{ {
printf " %b Unable to clone %s into %s, unable to continue%b\\n" "${COL_LIGHT_RED}" "${piholeGitUrl}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}" printf " %b Unable to clone %s into %s, unable to continue%b\\n" "${COL_RED}" "${piholeGitUrl}" "${PI_HOLE_LOCAL_REPO}" "${COL_NC}"
exit 1 exit 1
} }
# get the Web git files # get the Web git files
getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} ||
{ {
printf " %b Unable to clone %s into ${webInterfaceDir}, exiting installer%b\\n" "${COL_LIGHT_RED}" "${webInterfaceGitUrl}" "${COL_NC}" printf " %b Unable to clone %s into ${webInterfaceDir}, exiting installer%b\\n" "${COL_RED}" "${webInterfaceGitUrl}" "${COL_NC}"
exit 1 exit 1
} }
fi fi
@@ -1797,8 +1880,12 @@ FTLinstall() {
# Before stopping FTL, we download the macvendor database # Before stopping FTL, we download the macvendor database
curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true curl -sSL "https://ftl.pi-hole.net/macvendor.db" -o "${PI_HOLE_CONFIG_DIR}/macvendor.db" || true
# Stop pihole-FTL service if available
stop_service pihole-FTL >/dev/null # If the binary already exists in /usr/bin, then we need to stop the service
# If the binary does not exist (fresh installs), then we can skip this step.
if [[ -f /usr/bin/pihole-FTL ]]; then
stop_service pihole-FTL >/dev/null
fi
# Install the new version with the correct permissions # Install the new version with the correct permissions
install -T -m 0755 "${binary}" /usr/bin/pihole-FTL install -T -m 0755 "${binary}" /usr/bin/pihole-FTL
@@ -1823,7 +1910,7 @@ FTLinstall() {
return 1 return 1
} }
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b Error: Download of %s/%s failed (checksum error)%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}" printf " %b Error: Download of %s/%s failed (checksum error)%b\\n" "${COL_RED}" "${url}" "${binary}" "${COL_NC}"
# Remove temp dir # Remove temp dir
remove_dir "${tempdir}" remove_dir "${tempdir}"
@@ -1837,7 +1924,7 @@ FTLinstall() {
} }
printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s\\n" "${OVER}" "${CROSS}" "${str}"
# The URL could not be found # The URL could not be found
printf " %b Error: URL %s/%s not found%b\\n" "${COL_LIGHT_RED}" "${url}" "${binary}" "${COL_NC}" printf " %b Error: URL %s/%s not found%b\\n" "${COL_RED}" "${url}" "${binary}" "${COL_NC}"
# Remove temp dir # Remove temp dir
remove_dir "${tempdir}" remove_dir "${tempdir}"
@@ -1912,9 +1999,9 @@ get_binary_name() {
l_binary="pihole-FTL-riscv64" l_binary="pihole-FTL-riscv64"
else else
# Something else - we try to use 32bit executable and warn the user # Something else - we try to use 32bit executable and warn the user
if [[ ! "${machine}" == "i686" ]]; then if [[ "${machine}" != "i686" ]]; then
printf "%b %b %s...\\n" "${OVER}" "${CROSS}" "${str}" printf "%b %b %s...\\n" "${OVER}" "${CROSS}" "${str}"
printf " %b %bNot able to detect architecture (unknown: %s), trying x86 (32bit) executable%b\\n" "${INFO}" "${COL_LIGHT_RED}" "${machine}" "${COL_NC}" printf " %b %bNot able to detect architecture (unknown: %s), trying x86 (32bit) executable%b\\n" "${INFO}" "${COL_RED}" "${machine}" "${COL_NC}"
printf " %b Contact Pi-hole Support if you experience issues (e.g: FTL not running)\\n" "${INFO}" printf " %b Contact Pi-hole Support if you experience issues (e.g: FTL not running)\\n" "${INFO}"
else else
printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}" printf "%b %b Detected 32bit (i686) architecture\\n" "${OVER}" "${TICK}"
@@ -1946,18 +2033,18 @@ FTLcheckUpdate() {
local remoteSha1 local remoteSha1
local localSha1 local localSha1
if [[ ! "${ftlBranch}" == "master" ]]; then if [[ "${ftlBranch}" != "master" ]]; then
# This is not the master branch # This is not the master branch
local path local path
path="${ftlBranch}/${binary}" path="${ftlBranch}/${binary}"
# Check whether or not the binary for this FTL branch actually exists. If not, then there is no update! # Check whether or not the binary for this FTL branch actually exists. If not, then there is no update!
if ! check_download_exists "$path"; then local status
local status if ! check_download_exists "${path}"; then
status=$? status=$?
if [ "${status}" -eq 1 ]; then if [ "${status}" -eq 1 ]; then
printf " %b Branch \"%s\" is not available.\\n" "${INFO}" "${ftlBranch}" printf " %b Branch \"%s\" is not available.\\n" "${INFO}" "${ftlBranch}"
printf " %b Use %bpihole checkout ftl [branchname]%b to switch to a valid branch.\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${COL_NC}" printf " %b Use %bpihole checkout ftl [branchname]%b to switch to a valid branch.\\n" "${INFO}" "${COL_GREEN}" "${COL_NC}"
elif [ "${status}" -eq 2 ]; then elif [ "${status}" -eq 2 ]; then
printf " %b Unable to download from ftl.pi-hole.net. Please check your Internet connection and try again later.\\n" "${CROSS}" printf " %b Unable to download from ftl.pi-hole.net. Please check your Internet connection and try again later.\\n" "${CROSS}"
return 3 return 3
@@ -2043,6 +2130,11 @@ FTLdetect() {
if FTLcheckUpdate "${1}"; then if FTLcheckUpdate "${1}"; then
FTLinstall "${1}" || return 1 FTLinstall "${1}" || return 1
else
case $? in
1) :;; # FTL is up-to-date
*) exit 1;; # 404 (2), other HTTP or curl error (3), unknown (4)
esac
fi fi
} }
@@ -2051,11 +2143,11 @@ make_temporary_log() {
TEMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX) TEMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX)
# Open handle 3 for templog # Open handle 3 for templog
# https://stackoverflow.com/questions/18460186/writing-outputs-to-log-file-and-console # https://stackoverflow.com/questions/18460186/writing-outputs-to-log-file-and-console
exec 3>"$TEMPLOG" exec 3>"${TEMPLOG}"
# Delete templog, but allow for addressing via file handle # Delete templog, but allow for addressing via file handle
# This lets us write to the log without having a temporary file on the drive, which # This lets us write to the log without having a temporary file on the drive, which
# is meant to be a security measure so there is not a lingering file on the drive during the install process # is meant to be a security measure so there is not a lingering file on the drive during the install process
rm "$TEMPLOG" rm "${TEMPLOG}"
} }
copy_to_install_log() { copy_to_install_log() {
@@ -2170,7 +2262,7 @@ main() {
else else
# Otherwise, they do not have enough privileges, so let the user know # Otherwise, they do not have enough privileges, so let the user know
printf " %b %s\\n" "${INFO}" "${str}" printf " %b %s\\n" "${INFO}" "${str}"
printf " %b %bScript called with non-root privileges%b\\n" "${INFO}" "${COL_LIGHT_RED}" "${COL_NC}" printf " %b %bScript called with non-root privileges%b\\n" "${INFO}" "${COL_RED}" "${COL_NC}"
printf " The Pi-hole requires elevated privileges to install and run\\n" printf " The Pi-hole requires elevated privileges to install and run\\n"
printf " Please check the installer for any concerns regarding this requirement\\n" printf " Please check the installer for any concerns regarding this requirement\\n"
printf " Make sure to download this script from a trusted source\\n\\n" printf " Make sure to download this script from a trusted source\\n\\n"
@@ -2194,7 +2286,7 @@ main() {
# Otherwise, tell the user they need to run the script as root, and bail # Otherwise, tell the user they need to run the script as root, and bail
printf "%b %b Sudo utility check\\n" "${OVER}" "${CROSS}" printf "%b %b Sudo utility check\\n" "${OVER}" "${CROSS}"
printf " %b Sudo is needed for the Web Interface to run pihole commands\\n\\n" "${INFO}" printf " %b Sudo is needed for the Web Interface to run pihole commands\\n\\n" "${INFO}"
printf " %b %bPlease re-run this installer as root${COL_NC}\\n" "${INFO}" "${COL_LIGHT_RED}" printf " %b %bPlease re-run this installer as root${COL_NC}\\n" "${INFO}" "${COL_RED}"
exit 1 exit 1
fi fi
fi fi
@@ -2205,9 +2297,17 @@ main() {
# Check for availability of either the "service" or "systemctl" commands # Check for availability of either the "service" or "systemctl" commands
check_service_command check_service_command
# Check if this is a fresh install or an update/repair
check_fresh_install
# Check for supported package managers so that we may install dependencies # Check for supported package managers so that we may install dependencies
package_manager_detect package_manager_detect
# Update package cache only on apt based systems
if is_command apt-get; then
update_package_cache || exit 1
fi
# Notify user of package availability # Notify user of package availability
notify_package_updates_available notify_package_updates_available
@@ -2228,10 +2328,7 @@ main() {
exit 1 exit 1
fi fi
# in case of an update (can be a v5 -> v6 or v6 -> v6 update) or repair if [[ "${fresh_install}" == false ]]; then
if [[ -f "${PI_HOLE_V6_CONFIG}" ]] || [[ -f "/etc/pihole/setupVars.conf" ]]; then
# retain settings
fresh_install=false
# if it's running unattended, # if it's running unattended,
if [[ "${runUnattended}" == true ]]; then if [[ "${runUnattended}" == true ]]; then
printf " %b Performing unattended setup, no dialogs will be displayed\\n" "${INFO}" printf " %b Performing unattended setup, no dialogs will be displayed\\n" "${INFO}"
@@ -2340,7 +2437,7 @@ main() {
if [ -n "${PIHOLE_DNS_1}" ]; then if [ -n "${PIHOLE_DNS_1}" ]; then
local string="\"${PIHOLE_DNS_1}\"" local string="\"${PIHOLE_DNS_1}\""
[ -n "${PIHOLE_DNS_2}" ] && string+=", \"${PIHOLE_DNS_2}\"" [ -n "${PIHOLE_DNS_2}" ] && string+=", \"${PIHOLE_DNS_2}\""
setFTLConfigValue "dns.upstreams" "[ $string ]" setFTLConfigValue "dns.upstreams" "[ ${string} ]"
fi fi
if [ -n "${QUERY_LOGGING}" ]; then if [ -n "${QUERY_LOGGING}" ]; then
@@ -2379,8 +2476,10 @@ main() {
printf " %b If you have not done so already, the above IP should be set to static.\\n" "${INFO}" printf " %b If you have not done so already, the above IP should be set to static.\\n" "${INFO}"
printf " %b View the web interface at http://pi.hole:${WEBPORT}/admin or http://%s/admin\\n\\n" "${INFO}" "${IPV4_ADDRESS%/*}:${WEBPORT}" printf " %b View the web interface at http://pi.hole:${WEBPORT}/admin or http://%s/admin\\n\\n" "${INFO}" "${IPV4_ADDRESS%/*}:${WEBPORT}"
printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_LIGHT_GREEN}" "${pw}" "${COL_NC}" printf " %b Web Interface password: %b%s%b\\n" "${INFO}" "${COL_GREEN}" "${pw}" "${COL_NC}"
printf " %b This can be changed using 'pihole setpassword'\\n\\n" "${INFO}" printf " %b This can be changed using 'pihole setpassword'\\n\\n" "${INFO}"
printf " %b To allow your user to use all CLI functions without authentication, refer to\\n" "${INFO}"
printf " our documentation at: https://docs.pi-hole.net/main/post-install/\\n\\n"
# Final dialog message to the user # Final dialog message to the user
dialog --no-shadow --keep-tite \ dialog --no-shadow --keep-tite \
@@ -2389,7 +2488,11 @@ main() {
\\n\\nIPv4: ${IPV4_ADDRESS%/*}\ \\n\\nIPv4: ${IPV4_ADDRESS%/*}\
\\nIPv6: ${IPV6_ADDRESS:-"Not Configured"}\ \\nIPv6: ${IPV6_ADDRESS:-"Not Configured"}\
\\nIf you have not done so already, the above IP should be set to static.\ \\nIf you have not done so already, the above IP should be set to static.\
\\nView the web interface at http://pi.hole/admin:${WEBPORT} or http://${IPV4_ADDRESS%/*}:${WEBPORT}/admin\\n\\nYour Admin Webpage login password is ${pw}" "${r}" "${c}" \\nView the web interface at http://pi.hole:${WEBPORT}/admin or http://${IPV4_ADDRESS%/*}:${WEBPORT}/admin\\n\\nYour Admin Webpage login password is ${pw}\
\\n
\\n
\\nTo allow your user to use all CLI functions without authentication,\
\\nrefer to https://docs.pi-hole.net/main/post-install/" "${r}" "${c}"
INSTALL_TYPE="Installation" INSTALL_TYPE="Installation"
else else
@@ -2398,7 +2501,7 @@ main() {
# Display where the log file is # Display where the log file is
printf "\\n %b The install log is located at: %s\\n" "${INFO}" "${installLogLoc}" printf "\\n %b The install log is located at: %s\\n" "${INFO}" "${installLogLoc}"
printf " %b %b%s complete! %b\\n" "${TICK}" "${COL_LIGHT_GREEN}" "${INSTALL_TYPE}" "${COL_NC}" printf " %b %b%s complete! %b\\n" "${TICK}" "${COL_GREEN}" "${INSTALL_TYPE}" "${COL_NC}"
if [[ "${INSTALL_TYPE}" == "Update" ]]; then if [[ "${INSTALL_TYPE}" == "Update" ]]; then
printf "\\n" printf "\\n"

View File

@@ -12,20 +12,13 @@
source "/opt/pihole/COL_TABLE" source "/opt/pihole/COL_TABLE"
# shellcheck source="./advanced/Scripts/utils.sh" # shellcheck source="./advanced/Scripts/utils.sh"
source "/opt/pihole/utils.sh" source "/opt/pihole/utils.sh"
# getFTLConfigValue() from utils.sh
SKIP_INSTALL="true"
# shellcheck source="./automated install/basic-install.sh"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh"
# stop_service() is defined in basic-install.sh
ADMIN_INTERFACE_DIR=$(getFTLConfigValue "webserver.paths.webroot")$(getFTLConfigValue "webserver.paths.webhome")
readonly ADMIN_INTERFACE_DIR
while true; do while true; do
read -rp " ${QST} Are you sure you would like to remove ${COL_WHITE}Pi-hole${COL_NC}? [y/N] " answer read -rp " ${QST} Are you sure you would like to remove ${COL_BOLD}Pi-hole${COL_NC}? [y/N] " answer
case ${answer} in case ${answer} in
[Yy]* ) break;; [Yy]* ) break;;
* ) echo -e "${OVER} ${COL_LIGHT_GREEN}Uninstall has been canceled${COL_NC}"; exit 0;; * ) echo -e "${OVER} ${COL_GREEN}Uninstall has been canceled${COL_NC}"; exit 0;;
esac esac
done done
@@ -34,130 +27,200 @@ str="Root user check"
if [[ ${EUID} -eq 0 ]]; then if [[ ${EUID} -eq 0 ]]; then
echo -e " ${TICK} ${str}" echo -e " ${TICK} ${str}"
else else
# Check if sudo is actually installed echo -e " ${CROSS} ${str}
# If it isn't, exit because the uninstall can not complete Script called with non-root privileges
if [ -x "$(command -v sudo)" ]; then The Pi-hole requires elevated privileges to uninstall"
export SUDO="sudo" exit 1
else
echo -e " ${CROSS} ${str}
Script called with non-root privileges
The Pi-hole requires elevated privileges to uninstall"
exit 1
fi
fi fi
readonly PI_HOLE_FILES_DIR="/etc/.pihole" # Get paths for admin interface, log files and database files,
# to allow deletion where user has specified a non-default location
ADMIN_INTERFACE_DIR=$(getFTLConfigValue "webserver.paths.webroot")$(getFTLConfigValue "webserver.paths.webhome")
FTL_LOG=$(getFTLConfigValue "files.log.ftl")
DNSMASQ_LOG=$(getFTLConfigValue "files.log.dnsmasq")
WEBSERVER_LOG=$(getFTLConfigValue "files.log.webserver")
PIHOLE_DB=$(getFTLConfigValue "files.database")
GRAVITY_DB=$(getFTLConfigValue "files.gravity")
MACVENDOR_DB=$(getFTLConfigValue "files.macvendor")
PI_HOLE_LOCAL_REPO="/etc/.pihole"
# Setting SKIP_INSTALL="true" to source the installer functions without running them
SKIP_INSTALL="true" SKIP_INSTALL="true"
# shellcheck source="./automated install/basic-install.sh" # shellcheck source="./automated install/basic-install.sh"
source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" source "${PI_HOLE_LOCAL_REPO}/automated install/basic-install.sh"
# Functions and Variables sources from basic-install:
# package_manager_detect() sourced from basic-install.sh # package_manager_detect(), disable_service(), stop_service(),
package_manager_detect # restart service() and is_command()
# PI_HOLE_CONFIG_DIR PI_HOLE_INSTALL_DIR PI_HOLE_LOCAL_REPO
removeMetaPackage() { removeMetaPackage() {
# Purge Pi-hole meta package # Purge Pi-hole meta package
echo "" echo ""
echo -ne " ${INFO} Removing Pi-hole meta package..."; echo -ne " ${INFO} Removing Pi-hole meta package...";
eval "${SUDO}" "${PKG_REMOVE}" "pihole-meta" &> /dev/null; eval "${PKG_REMOVE}" "pihole-meta" &> /dev/null;
echo -e "${OVER} ${INFO} Removed Pi-hole meta package"; echo -e "${OVER} ${INFO} Removed Pi-hole meta package";
} }
removePiholeFiles() { removeWebInterface() {
# Remove the web interface of Pi-hole # Remove the web interface of Pi-hole
echo -ne " ${INFO} Removing Web Interface..." echo -ne " ${INFO} Removing Web Interface..."
${SUDO} rm -rf "${ADMIN_INTERFACE_DIR}" &> /dev/null rm -rf "${ADMIN_INTERFACE_DIR:-/var/www/html/admin/}" &> /dev/null
echo -e "${OVER} ${TICK} Removed Web Interface" echo -e "${OVER} ${TICK} Removed Web Interface"
}
# Attempt to preserve backwards compatibility with older versions removeFTL() {
# to guarantee no additional changes were made to /etc/crontab after # Remove FTL and stop any running FTL service
# the installation of pihole, /etc/crontab.pihole should be permanently if is_command "pihole-FTL"; then
# preserved. # service stop & disable from basic_install.sh
if [[ -f /etc/crontab.orig ]]; then
${SUDO} mv /etc/crontab /etc/crontab.pihole
${SUDO} mv /etc/crontab.orig /etc/crontab
${SUDO} service cron restart
echo -e " ${TICK} Restored the default system cron"
fi
# Attempt to preserve backwards compatibility with older versions
if [[ -f /etc/cron.d/pihole ]];then
${SUDO} rm -f /etc/cron.d/pihole &> /dev/null
echo -e " ${TICK} Removed /etc/cron.d/pihole"
fi
${SUDO} rm -rf /var/log/*pihole* &> /dev/null
${SUDO} rm -rf /var/log/pihole/*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 -f /usr/local/bin/pihole &> /dev/null
${SUDO} rm -f /etc/bash_completion.d/pihole &> /dev/null
${SUDO} rm -f /etc/sudoers.d/pihole &> /dev/null
echo -e " ${TICK} Removed config files"
# Restore Resolved
if [[ -e /etc/systemd/resolved.conf.orig ]] || [[ -e /etc/systemd/resolved.conf.d/90-pi-hole-disable-stub-listener.conf ]]; then
${SUDO} cp -p /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf &> /dev/null || true
${SUDO} rm -f /etc/systemd/resolved.conf.d/90-pi-hole-disable-stub-listener.conf
systemctl reload-or-restart systemd-resolved
fi
# Remove FTL
if command -v pihole-FTL &> /dev/null; then
echo -ne " ${INFO} Removing pihole-FTL..."
stop_service pihole-FTL stop_service pihole-FTL
${SUDO} rm -f /etc/systemd/system/pihole-FTL.service disable_service pihole-FTL
echo -ne " ${INFO} Removing pihole-FTL..."
rm -f /etc/systemd/system/pihole-FTL.service &> /dev/null
if [[ -d '/etc/systemd/system/pihole-FTL.service.d' ]]; then if [[ -d '/etc/systemd/system/pihole-FTL.service.d' ]]; then
read -rp " ${QST} FTL service override directory /etc/systemd/system/pihole-FTL.service.d detected. Do you wish to remove this from your system? [y/N] " answer read -rp " ${QST} FTL service override directory /etc/systemd/system/pihole-FTL.service.d detected. Do you wish to remove this from your system? [y/N] " answer
case $answer in case $answer in
[yY]*) [yY]*)
echo -ne " ${INFO} Removing /etc/systemd/system/pihole-FTL.service.d..." echo -ne " ${INFO} Removing /etc/systemd/system/pihole-FTL.service.d..."
${SUDO} rm -R /etc/systemd/system/pihole-FTL.service.d rm -R /etc/systemd/system/pihole-FTL.service.d &> /dev/null
echo -e "${OVER} ${INFO} Removed /etc/systemd/system/pihole-FTL.service.d" echo -e "${OVER} ${INFO} Removed /etc/systemd/system/pihole-FTL.service.d"
;; ;;
*) echo -e " ${INFO} Leaving /etc/systemd/system/pihole-FTL.service.d in place.";; *) echo -e " ${INFO} Leaving /etc/systemd/system/pihole-FTL.service.d in place.";;
esac esac
fi fi
${SUDO} rm -f /etc/init.d/pihole-FTL rm -f /etc/init.d/pihole-FTL &> /dev/null
${SUDO} rm -f /usr/bin/pihole-FTL rm -f /usr/bin/pihole-FTL &> /dev/null
echo -e "${OVER} ${TICK} Removed pihole-FTL" echo -e "${OVER} ${TICK} Removed pihole-FTL"
# Force systemd reload after service files are removed
if is_command "systemctl"; then
echo -ne " ${INFO} Restarting systemd..."
systemctl daemon-reload
echo -e "${OVER} ${TICK} Restarted systemd..."
fi
fi
}
removeCronFiles() {
# Attempt to preserve backwards compatibility with older versions
# to guarantee no additional changes were made to /etc/crontab after
# the installation of pihole, /etc/crontab.pihole should be permanently
# preserved.
if [[ -f /etc/crontab.orig ]]; then
mv /etc/crontab /etc/crontab.pihole
mv /etc/crontab.orig /etc/crontab
restart_service cron
echo -e " ${TICK} Restored the default system cron"
echo -e " ${INFO} A backup of the most recent crontab is saved at /etc/crontab.pihole"
fi fi
# If the pihole manpage exists, then delete and rebuild man-db # Attempt to preserve backwards compatibility with older versions
if [[ -f /etc/cron.d/pihole ]];then
rm -f /etc/cron.d/pihole &> /dev/null
echo -e " ${TICK} Removed /etc/cron.d/pihole"
fi
}
removePiholeFiles() {
# Remove databases (including user specified non-default paths)
rm -f "${PIHOLE_DB:-/etc/pihole/pihole-FTL.db}" &> /dev/null
rm -f "${GRAVITY_DB:-/etc/pihole/gravity.db}" &> /dev/null
rm -f "${MACVENDOR_DB:-/etc/pihole/macvendor.db}" &> /dev/null
# Remove pihole config, repo and local files
rm -rf "${PI_HOLE_CONFIG_DIR:-/etc/pihole}" &> /dev/null
rm -rf "${PI_HOLE_LOCAL_REPO:-/etc/.pihole}" &> /dev/null
rm -rf "${PI_HOLE_INSTALL_DIR:-/opt/pihole}" &> /dev/null
# Remove log files (including user specified non-default paths)
# and rotated logs
# Explicitly escape spaces, in case of trailing space in path before wildcard
rm -f "$(printf '%q' "${FTL_LOG:-/var/log/pihole/FTL.log}")*" &> /dev/null
rm -f "$(printf '%q' "${DNSMASQ_LOG:-/var/log/pihole/pihole.log}")*" &> /dev/null
rm -f "$(printf '%q' "${WEBSERVER_LOG:-/var/log/pihole/webserver.log}")*" &> /dev/null
# remove any remnant log-files from old versions
rm -rf /var/log/*pihole* &> /dev/null
# remove log directory
rm -rf /var/log/pihole &> /dev/null
# remove the pihole command
rm -f /usr/local/bin/pihole &> /dev/null
# remove Pi-hole's bash completion
rm -f /etc/bash_completion.d/pihole &> /dev/null
rm -f /etc/bash_completion.d/pihole-FTL &> /dev/null
# Remove pihole from sudoers for compatibility with old versions
rm -f /etc/sudoers.d/pihole &> /dev/null
echo -e " ${TICK} Removed config files"
}
removeManPage() {
# If the pihole manpage exists, then delete
if [[ -f /usr/local/share/man/man8/pihole.8 ]]; then if [[ -f /usr/local/share/man/man8/pihole.8 ]]; then
${SUDO} rm -f /usr/local/share/man/man8/pihole.8 /usr/local/share/man/man8/pihole-FTL.8 /usr/local/share/man/man5/pihole-FTL.conf.5 rm -f /usr/local/share/man/man8/pihole.8 /usr/local/share/man/man8/pihole-FTL.8 /usr/local/share/man/man5/pihole-FTL.conf.5
${SUDO} mandb -q &>/dev/null # Rebuild man-db if present
if is_command "mandb"; then
mandb -q &>/dev/null
fi
echo -e " ${TICK} Removed pihole man page" echo -e " ${TICK} Removed pihole man page"
fi fi
}
removeUser() {
# If the pihole user exists, then remove # If the pihole user exists, then remove
if id "pihole" &> /dev/null; then if id "pihole" &> /dev/null; then
if ${SUDO} userdel -r pihole 2> /dev/null; then if userdel -r pihole 2> /dev/null; then
echo -e " ${TICK} Removed 'pihole' user" echo -e " ${TICK} Removed 'pihole' user"
else else
echo -e " ${CROSS} Unable to remove 'pihole' user" echo -e " ${CROSS} Unable to remove 'pihole' user"
fi fi
fi fi
# If the pihole group exists, then remove # If the pihole group exists, then remove
if getent group "pihole" &> /dev/null; then if getent group "pihole" &> /dev/null; then
if ${SUDO} groupdel pihole 2> /dev/null; then if groupdel pihole 2> /dev/null; then
echo -e " ${TICK} Removed 'pihole' group" echo -e " ${TICK} Removed 'pihole' group"
else else
echo -e " ${CROSS} Unable to remove 'pihole' group" echo -e " ${CROSS} Unable to remove 'pihole' group"
fi fi
fi fi
}
restoreResolved() {
# Restore Resolved from saved configuration, if present
if [[ -e /etc/systemd/resolved.conf.orig ]] || [[ -e /etc/systemd/resolved.conf.d/90-pi-hole-disable-stub-listener.conf ]]; then
cp -p /etc/systemd/resolved.conf.orig /etc/systemd/resolved.conf &> /dev/null || true
rm -f /etc/systemd/resolved.conf.d/90-pi-hole-disable-stub-listener.conf &> /dev/null
systemctl reload-or-restart systemd-resolved
fi
}
completionMessage() {
echo -e "\\n We're sorry to see you go, but thanks for checking out Pi-hole! 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 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} Reinstall at any time: ${COL_BOLD}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_NC} ${COL_RED}Please reset the DNS on your router/clients to restore internet connectivity${COL_NC}
${INFO} Pi-hole's meta package has been removed, use the 'autoremove' function from your package manager to remove unused dependencies${COL_NC} ${INFO} Pi-hole's meta package has been removed, use the 'autoremove' function from your package manager to remove unused dependencies${COL_NC}
${COL_LIGHT_GREEN}Uninstallation Complete! ${COL_NC}" ${COL_GREEN}Uninstallation Complete! ${COL_NC}"
} }
######### SCRIPT ########### ######### SCRIPT ###########
# The ordering here allows clean uninstallation with nothing
# removed before anything that depends upon it.
# eg removeFTL relies on scripts removed by removePiholeFiles
# removeUser relies on commands removed by removeMetaPackage
package_manager_detect
removeWebInterface
removeCronFiles
restoreResolved
removeManPage
removeFTL
removeUser
removeMetaPackage removeMetaPackage
removePiholeFiles removePiholeFiles
completionMessage

View File

@@ -50,7 +50,7 @@ etag_support=false
# Check gravity temp directory # Check gravity temp directory
if [ ! -d "${GRAVITY_TMPDIR}" ] || [ ! -w "${GRAVITY_TMPDIR}" ]; then if [ ! -d "${GRAVITY_TMPDIR}" ] || [ ! -w "${GRAVITY_TMPDIR}" ]; then
echo -e " ${COL_LIGHT_RED}Gravity temporary directory does not exist or is not a writeable directory, falling back to /tmp. ${COL_NC}" echo -e " ${COL_RED}Gravity temporary directory does not exist or is not a writeable directory, falling back to /tmp. ${COL_NC}"
GRAVITY_TMPDIR="/tmp" GRAVITY_TMPDIR="/tmp"
fi fi
@@ -118,9 +118,12 @@ gravity_swap_databases() {
# Swap databases and remove or conditionally rename old database # Swap databases and remove or conditionally rename old database
# Number of available blocks on disk # Number of available blocks on disk
availableBlocks=$(stat -f --format "%a" "${gravityDIR}") # Busybox Compat: `stat` long flags unsupported
# -f flag is short form of --file-system.
# -c flag is short form of --format.
availableBlocks=$(stat -f -c "%a" "${gravityDIR}")
# Number of blocks, used by gravity.db # Number of blocks, used by gravity.db
gravityBlocks=$(stat --format "%b" "${gravityDBfile}") gravityBlocks=$(stat -c "%b" "${gravityDBfile}")
# Only keep the old database if available disk space is at least twice the size of the existing gravity.db. # Only keep the old database if available disk space is at least twice the size of the existing gravity.db.
# Better be safe than sorry... # Better be safe than sorry...
oldAvail=false oldAvail=false
@@ -608,8 +611,10 @@ compareLists() {
# Download specified URL and perform checks on HTTP status and file content # Download specified URL and perform checks on HTTP status and file content
gravity_DownloadBlocklistFromUrl() { gravity_DownloadBlocklistFromUrl() {
local url="${1}" adlistID="${2}" saveLocation="${3}" compression="${4}" gravity_type="${5}" domain="${6}" local url="${1}" adlistID="${2}" saveLocation="${3}" compression="${4}" gravity_type="${5}" domain="${6}"
local modifiedOptions="" listCurlBuffer str httpCode success="" ip cmd_ext local listCurlBuffer str curlJson httpCode curlErrorMsg="" curlExitCode="" success="" ip customUpstreamResolver=""
local file_path permissions ip_addr port blocked=false download=true local file_path permissions ip_addr port blocked=false download=true
# modifiedOptions is an array to store all the options used to check if the adlist has been changed upstream
local modifiedOptions=()
# Create temp file to store content on disk instead of RAM # Create temp file to store content on disk instead of RAM
# We don't use '--suffix' here because not all implementations of mktemp support it, e.g. on Alpine # We don't use '--suffix' here because not all implementations of mktemp support it, e.g. on Alpine
@@ -626,14 +631,14 @@ gravity_DownloadBlocklistFromUrl() {
# Save HTTP ETag to the specified file. An ETag is a caching related header, # Save HTTP ETag to the specified file. An ETag is a caching related header,
# usually returned in a response. If no ETag is sent by the server, an empty # usually returned in a response. If no ETag is sent by the server, an empty
# file is created and can later be used consistently. # file is created and can later be used consistently.
modifiedOptions="--etag-save ${saveLocation}.etag" modifiedOptions=("${modifiedOptions[@]}" --etag-save "${saveLocation}".etag)
if [[ -f "${saveLocation}.etag" ]]; then if [[ -f "${saveLocation}.etag" ]]; then
# This option makes a conditional HTTP request for the specific ETag read # This option makes a conditional HTTP request for the specific ETag read
# from the given file by sending a custom If-None-Match header using the # from the given file by sending a custom If-None-Match header using the
# stored ETag. This way, the server will only send the file if it has # stored ETag. This way, the server will only send the file if it has
# changed since the last request. # changed since the last request.
modifiedOptions="${modifiedOptions} --etag-compare ${saveLocation}.etag" modifiedOptions=("${modifiedOptions[@]}" --etag-compare "${saveLocation}".etag)
fi fi
fi fi
@@ -646,7 +651,7 @@ gravity_DownloadBlocklistFromUrl() {
# Interstingly, this option is not supported by raw.githubusercontent.com # Interstingly, this option is not supported by raw.githubusercontent.com
# URLs, however, it is still supported by many older web servers which may # URLs, however, it is still supported by many older web servers which may
# not support the HTTP ETag method so we keep it as a fallback. # not support the HTTP ETag method so we keep it as a fallback.
modifiedOptions="${modifiedOptions} -z ${saveLocation}" modifiedOptions=("${modifiedOptions[@]}" -z "${saveLocation}")
fi fi
fi fi
@@ -712,7 +717,7 @@ gravity_DownloadBlocklistFromUrl() {
fi fi
echo -e "${OVER} ${CROSS} ${str} ${domain} is blocked by one of your lists. Using DNS server ${upstream} instead" echo -e "${OVER} ${CROSS} ${str} ${domain} is blocked by one of your lists. Using DNS server ${upstream} instead"
echo -ne " ${INFO} ${str} Pending..." echo -ne " ${INFO} ${str} Pending..."
cmd_ext="--resolve $domain:$port:$ip" customUpstreamResolver="--resolve $domain:$port:$ip"
fi fi
fi fi
@@ -750,11 +755,14 @@ gravity_DownloadBlocklistFromUrl() {
fi fi
if [[ "${download}" == true ]]; then if [[ "${download}" == true ]]; then
# See https://github.com/pi-hole/pi-hole/issues/6159 for justification of the below disable directive curlJson=$(curl --connect-timeout ${curl_connect_timeout} -s -L ${compression:+${compression}} ${customUpstreamResolver:+${customUpstreamResolver}} "${modifiedOptions[@]}" -w "%{json}" "${url}" -o "${listCurlBuffer}")
# shellcheck disable=SC2086
httpCode=$(curl --connect-timeout ${curl_connect_timeout} -s -L ${compression} ${cmd_ext} ${modifiedOptions} -w "%{http_code}" "${url}" -o "${listCurlBuffer}" 2>/dev/null)
fi fi
# Retrieve the HTTP code, exit code and error message returned by curl command
httpCode=$(echo "${curlJson}" | jq '.http_code')
curlErrorMsg=$(echo "${curlJson}" | jq '.errormsg')
curlExitCode=$(echo "${curlJson}" | jq '.exitcode')
case $url in case $url in
# Did we "download" a local file? # Did we "download" a local file?
"file"*) "file"*)
@@ -777,7 +785,6 @@ gravity_DownloadBlocklistFromUrl() {
echo -e "${OVER} ${TICK} ${str} No changes detected" echo -e "${OVER} ${TICK} ${str} No changes detected"
success=true success=true
;; ;;
"000") echo -e "${OVER} ${CROSS} ${str} Connection Refused" ;;
"403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;; "403") echo -e "${OVER} ${CROSS} ${str} Forbidden" ;;
"404") echo -e "${OVER} ${CROSS} ${str} Not found" ;; "404") echo -e "${OVER} ${CROSS} ${str} Not found" ;;
"408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;; "408") echo -e "${OVER} ${CROSS} ${str} Time-out" ;;
@@ -786,7 +793,7 @@ gravity_DownloadBlocklistFromUrl() {
"504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;; "504") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Gateway)" ;;
"521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;; "521") echo -e "${OVER} ${CROSS} ${str} Web Server Is Down (Cloudflare)" ;;
"522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;; "522") echo -e "${OVER} ${CROSS} ${str} Connection Timed Out (Cloudflare)" ;;
*) echo -e "${OVER} ${CROSS} ${str} ${url} (${httpCode})" ;; *) echo -e "${OVER} ${CROSS} ${str} Failure (exit_code=${COL_RED}${curlExitCode}${COL_NC} Msg: ${COL_CYAN}${curlErrorMsg}${COL_NC})" ;;
esac esac
;; ;;
esac esac
@@ -821,13 +828,13 @@ gravity_DownloadBlocklistFromUrl() {
if [[ "${done}" != "true" ]]; then if [[ "${done}" != "true" ]]; then
# Determine if cached list has read permission # Determine if cached list has read permission
if [[ -r "${saveLocation}" ]]; then if [[ -r "${saveLocation}" ]]; then
echo -e " ${CROSS} List download failed: ${COL_LIGHT_GREEN}using previously cached list${COL_NC}" echo -e " ${CROSS} List download failed: ${COL_GREEN}using previously cached list${COL_NC}"
# Set list status to "download-failed/cached" # Set list status to "download-failed/cached"
database_adlist_status "${adlistID}" "3" database_adlist_status "${adlistID}" "3"
# Add domains to database table file # Add domains to database table file
pihole-FTL "${gravity_type}" parseList "${saveLocation}" "${gravityTEMPfile}" "${adlistID}" pihole-FTL "${gravity_type}" parseList "${saveLocation}" "${gravityTEMPfile}" "${adlistID}"
else else
echo -e " ${CROSS} List download failed: ${COL_LIGHT_RED}no cached list available${COL_NC}" echo -e " ${CROSS} List download failed: ${COL_RED}no cached list available${COL_NC}"
# Manually reset these two numbers because we do not call parseList here # Manually reset these two numbers because we do not call parseList here
database_adlist_number "${adlistID}" 0 0 database_adlist_number "${adlistID}" 0 0
database_adlist_status "${adlistID}" "4" database_adlist_status "${adlistID}" "4"
@@ -851,7 +858,7 @@ gravity_Table_Count() {
fi fi
} }
# Output count of blacklisted domains and regex filters # Output count of denied and allowed domains and regex filters
gravity_ShowCount() { gravity_ShowCount() {
# Here we use the table "gravity" instead of the view "vw_gravity" for speed. # Here we use the table "gravity" instead of the view "vw_gravity" for speed.
# It's safe to replace it here, because right after a gravity run both will show the exactly same number of domains. # It's safe to replace it here, because right after a gravity run both will show the exactly same number of domains.
@@ -864,7 +871,7 @@ gravity_ShowCount() {
# Trap Ctrl-C # Trap Ctrl-C
gravity_Trap() { gravity_Trap() {
trap '{ echo -e "\\n\\n ${INFO} ${COL_LIGHT_RED}User-abort detected${COL_NC}"; gravity_Cleanup "error"; }' INT trap '{ echo -e "\\n\\n ${INFO} ${COL_RED}User-abort detected${COL_NC}"; gravity_Cleanup "error"; }' INT
} }
# Clean up after Gravity upon exit or cancellation # Clean up after Gravity upon exit or cancellation

View File

@@ -105,9 +105,9 @@ Available commands and options:
Flush the Pi-hole log Flush the Pi-hole log
.br .br
\fB-r, reconfigure\fR \fB-r, repair\fR
.br .br
Reconfigure or Repair Pi-hole subsystems Repair Pi-hole subsystems
.br .br
\fB-t, tail\fR [arg] \fB-t, tail\fR [arg]
@@ -268,7 +268,7 @@ Allow-/denylist manipulation
\fBpihole --regex "ad.*\\.example\\.com$"\fR \fBpihole --regex "ad.*\\.example\\.com$"\fR
.br .br
Adds "ad.*\\.example\\.com$" to the regex blacklist. Adds "ad.*\\.example\\.com$" to the regex denylist.
Would block all subdomains of example.com which start with "ad" Would block all subdomains of example.com which start with "ad"
.br .br
@@ -317,9 +317,10 @@ Switching Pi-hole subsystem branches
Switch to core development branch Switch to core development branch
.br .br
\fBpihole arpflush\fR \fBpihole networkflush\fR
.br .br
Flush information stored in Pi-hole's network tables Flush information stored in Pi-hole's network table
Add '--arp' to additionally flush the ARP table
.br .br
\fBpihole api stats/summary\fR \fBpihole api stats/summary\fR

104
pihole
View File

@@ -9,7 +9,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.
readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" PI_HOLE_SCRIPT_DIR="/opt/pihole"
# PI_HOLE_BIN_DIR is not readonly here because in some functions (checkout), # PI_HOLE_BIN_DIR is not readonly here because in some functions (checkout),
# they might get set again when the installer is sourced. This causes an # they might get set again when the installer is sourced. This causes an
@@ -20,7 +20,7 @@ readonly colfile="${PI_HOLE_SCRIPT_DIR}/COL_TABLE"
# shellcheck source=./advanced/Scripts/COL_TABLE # shellcheck source=./advanced/Scripts/COL_TABLE
source "${colfile}" source "${colfile}"
readonly utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh" utilsfile="${PI_HOLE_SCRIPT_DIR}/utils.sh"
# shellcheck source=./advanced/Scripts/utils.sh # shellcheck source=./advanced/Scripts/utils.sh
source "${utilsfile}" source "${utilsfile}"
@@ -96,8 +96,18 @@ flushFunc() {
exit 0 exit 0
} }
# Deprecated function, should be removed in the future
# use networkFlush instead
arpFunc() { arpFunc() {
"${PI_HOLE_SCRIPT_DIR}"/piholeARPTable.sh "$@" shift
echo -e " ${INFO} The 'arpflush' command is deprecated, use 'networkflush' instead"
"${PI_HOLE_SCRIPT_DIR}"/piholeNetworkFlush.sh "$@"
exit 0
}
networkFlush() {
shift
"${PI_HOLE_SCRIPT_DIR}"/piholeNetworkFlush.sh "$@"
exit 0 exit 0
} }
@@ -147,10 +157,11 @@ uninstallFunc() {
versionFunc() { versionFunc() {
exec "${PI_HOLE_SCRIPT_DIR}"/version.sh exec "${PI_HOLE_SCRIPT_DIR}"/version.sh
exit 0
} }
reloadDNS() { reloadDNS() {
local svcOption svc str output status pid icon FTL_PID_FILE local svcOption svc str output status pid icon FTL_PID_FILE sigrtmin
svcOption="${1:-reload}" svcOption="${1:-reload}"
# get the current path to the pihole-FTL.pid # get the current path to the pihole-FTL.pid
@@ -169,7 +180,10 @@ reloadDNS() {
str="FTL is not running" str="FTL is not running"
icon="${INFO}" icon="${INFO}"
else else
svc="kill -RTMIN ${pid}" sigrtmin="$(pihole-FTL sigrtmin 2>/dev/null)"
# Make sure sigrtmin is a number, otherwise fallback to RTMIN
[[ "${sigrtmin}" =~ ^[0-9]+$ ]] || unset sigrtmin
svc="kill -${sigrtmin:-RTMIN} ${pid}"
str="Reloading DNS lists" str="Reloading DNS lists"
icon="${TICK}" icon="${TICK}"
fi fi
@@ -238,7 +252,7 @@ Time:
fi fi
if [[ ${error} == true ]];then if [[ ${error} == true ]];then
echo -e " ${COL_LIGHT_RED}Unknown format for blocking timer!${COL_NC}" echo -e " ${COL_RED}Unknown format for blocking timer!${COL_NC}"
echo -e " Try 'pihole disable --help' for more information." echo -e " Try 'pihole disable --help' for more information."
exit 1 exit 1
fi fi
@@ -264,6 +278,7 @@ Time:
LogoutAPI LogoutAPI
echo -e "${OVER} ${TICK} ${str}" echo -e "${OVER} ${TICK} ${str}"
exit 0
} }
piholeLogging() { piholeLogging() {
@@ -293,7 +308,7 @@ Options:
echo -e " ${INFO} Enabling logging..." echo -e " ${INFO} Enabling logging..."
local str="Logging has been enabled!" local str="Logging has been enabled!"
else else
echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC} echo -e " ${COL_RED}Invalid option${COL_NC}
Try 'pihole logging --help' for more information." Try 'pihole logging --help' for more information."
exit 1 exit 1
fi fi
@@ -389,14 +404,14 @@ tailFunc() {
echo -e " ${INFO} Press Ctrl-C to exit" echo -e " ${INFO} Press Ctrl-C to exit"
# Get logfile path # Get logfile path
readonly LOGFILE
LOGFILE=$(getFTLConfigValue files.log.dnsmasq) LOGFILE=$(getFTLConfigValue files.log.dnsmasq)
readonly LOGFILE
# Strip date from each line # Strip date from each line
# Color blocklist/denylist/wildcard entries as red # Color blocklist/denylist/wildcard entries as red
# Color A/AAAA/DHCP strings as white # Color A/AAAA/DHCP strings as white
# Color everything else as gray # Color everything else as gray
tail -f $LOGFILE | grep --line-buffered "${1}" | sed -E \ tail -f $LOGFILE | grep --line-buffered -- "${1}" | sed -E \
-e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \ -e "s,($(date +'%b %d ')| dnsmasq\[[0-9]*\]),,g" \
-e "s,(.*(denied |gravity blocked ).*),${COL_RED}&${COL_NC}," \ -e "s,(.*(denied |gravity blocked ).*),${COL_RED}&${COL_NC}," \
-e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \ -e "s,.*(query\\[A|DHCP).*,${COL_NC}&${COL_NC}," \
@@ -519,7 +534,8 @@ Options:
reloadlists Update the lists WITHOUT flushing the cache or restarting the DNS server reloadlists Update the lists WITHOUT flushing the cache or restarting the DNS server
checkout Switch Pi-hole subsystems to a different GitHub branch checkout Switch Pi-hole subsystems to a different GitHub branch
Add '-h' for more info on checkout usage Add '-h' for more info on checkout usage
arpflush Flush information stored in Pi-hole's network tables"; networkflush Flush information stored in Pi-hole's network tables
Add '--arp' to additionally flush the ARP table ";
exit 0 exit 0
} }
@@ -528,7 +544,7 @@ if [[ $# = 0 ]]; then
fi fi
# functions that do not require sudo power # functions that do not require sudo power
need_root=1 need_root=
case "${1}" in case "${1}" in
"-h" | "help" | "--help" ) helpFunc;; "-h" | "help" | "--help" ) helpFunc;;
"-v" | "version" ) versionFunc;; "-v" | "version" ) versionFunc;;
@@ -536,31 +552,32 @@ case "${1}" in
"-q" | "query" ) queryFunc "$@";; "-q" | "query" ) queryFunc "$@";;
"status" ) statusFunc "$2";; "status" ) statusFunc "$2";;
"tricorder" ) tricorderFunc;; "tricorder" ) tricorderFunc;;
"allow" | "allowlist" ) listFunc "$@";;
"deny" | "denylist" ) listFunc "$@";;
"--wild" | "wildcard" ) listFunc "$@";;
"--regex" | "regex" ) listFunc "$@";;
"--allow-regex" | "allow-regex" ) listFunc "$@";;
"--allow-wild" | "allow-wild" ) listFunc "$@";;
"enable" ) piholeEnable true "$2";;
"disable" ) piholeEnable false "$2";;
"api" ) shift; apiFunc "$@"; exit 0;;
# we need to add all arguments that require sudo power to not trigger the * argument # we need to add all arguments that require sudo power to not trigger the * argument
"allow" | "allowlist" ) need_root=0;; "-f" | "flush" ) need_root=true;;
"deny" | "denylist" ) need_root=0;; "-up" | "updatePihole" ) need_root=true;;
"--wild" | "wildcard" ) need_root=0;; "-r" | "repair" ) need_root=true;;
"--regex" | "regex" ) need_root=0;; "-l" | "logging" ) need_root=true;;
"--allow-regex" | "allow-regex" ) need_root=0;; "uninstall" ) need_root=true;;
"--allow-wild" | "allow-wild" ) need_root=0;; "-d" | "debug" ) need_root=true;;
"-f" | "flush" ) ;; "-g" | "updateGravity" ) need_root=true;;
"-up" | "updatePihole" ) ;; "reloaddns" ) need_root=true;;
"-r" | "repair" ) ;; "reloadlists" ) need_root=true;;
"-l" | "logging" ) ;; "setpassword" ) need_root=true;;
"uninstall" ) ;; "checkout" ) need_root=true;;
"enable" ) need_root=0;; "updatechecker" ) need_root=true;;
"disable" ) need_root=0;; "arpflush" ) need_root=true;; # Deprecated, use networkflush instead
"-d" | "debug" ) ;; "networkflush" ) need_root=true;;
"-g" | "updateGravity" ) ;; "-t" | "tail" ) need_root=true;;
"reloaddns" ) ;;
"reloadlists" ) ;;
"setpassword" ) ;;
"checkout" ) ;;
"updatechecker" ) ;;
"arpflush" ) ;;
"-t" | "tail" ) ;;
"api" ) need_root=0;;
* ) helpFunc;; * ) helpFunc;;
esac esac
@@ -570,22 +587,17 @@ if [[ -z ${USER} ]]; then
USER=$(whoami) USER=$(whoami)
fi fi
# Check if the current user is neither root nor pihole and if the command # Check if the current user is not root and if the command
# requires root. If so, exit with an error message. # requires root. If so, exit with an error message.
if [[ $EUID -ne 0 && ${USER} != "pihole" && need_root -eq 1 ]];then # Add an exception for the user "pihole" to allow the webserver running gravity
echo -e " ${CROSS} The Pi-hole command requires root privileges, try:" if [[ ( $EUID -ne 0 && ${USER} != "pihole" ) && -n "${need_root}" ]]; then
echo -e " ${CROSS} This Pi-hole command requires root privileges, try:"
echo -e " ${COL_GREEN}sudo pihole $*${COL_NC}" echo -e " ${COL_GREEN}sudo pihole $*${COL_NC}"
exit 1 exit 1
fi fi
# Handle redirecting to specific functions based on arguments # Handle redirecting to specific functions based on arguments
case "${1}" in case "${1}" in
"allow" | "allowlist" ) listFunc "$@";;
"deny" | "denylist" ) listFunc "$@";;
"--wild" | "wildcard" ) listFunc "$@";;
"--regex" | "regex" ) listFunc "$@";;
"--allow-regex" | "allow-regex" ) listFunc "$@";;
"--allow-wild" | "allow-wild" ) listFunc "$@";;
"-d" | "debug" ) debugFunc "$@";; "-d" | "debug" ) debugFunc "$@";;
"-f" | "flush" ) flushFunc "$@";; "-f" | "flush" ) flushFunc "$@";;
"-up" | "updatePihole" ) updatePiholeFunc "$@";; "-up" | "updatePihole" ) updatePiholeFunc "$@";;
@@ -593,15 +605,13 @@ case "${1}" in
"-g" | "updateGravity" ) updateGravityFunc "$@";; "-g" | "updateGravity" ) updateGravityFunc "$@";;
"-l" | "logging" ) piholeLogging "$@";; "-l" | "logging" ) piholeLogging "$@";;
"uninstall" ) uninstallFunc;; "uninstall" ) uninstallFunc;;
"enable" ) piholeEnable true "$2";;
"disable" ) piholeEnable false "$2";;
"reloaddns" ) reloadDNS "reload";; "reloaddns" ) reloadDNS "reload";;
"reloadlists" ) reloadDNS "reload-lists";; "reloadlists" ) reloadDNS "reload-lists";;
"setpassword" ) SetWebPassword "$@";; "setpassword" ) SetWebPassword "$@";;
"checkout" ) piholeCheckoutFunc "$@";; "checkout" ) piholeCheckoutFunc "$@";;
"updatechecker" ) shift; updateCheckFunc "$@";; "updatechecker" ) shift; updateCheckFunc "$@";;
"arpflush" ) arpFunc "$@";; "arpflush" ) arpFunc "$@";; # Deprecated, use networkflush instead
"networkflush" ) networkFlush "$@";;
"-t" | "tail" ) tailFunc "$2";; "-t" | "tail" ) tailFunc "$2";;
"api" ) shift; apiFunc "$@";;
* ) helpFunc;; * ) helpFunc;;
esac esac

View File

@@ -0,0 +1,18 @@
FROM alpine:3.21
ENV GITDIR=/etc/.pihole
ENV SCRIPTDIR=/opt/pihole
RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories
RUN apk --no-cache add bash coreutils curl git jq openrc shadow
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $GITDIR/advanced/Scripts/COL_TABLE $SCRIPTDIR/
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV SKIP_INSTALL=true
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -0,0 +1,18 @@
FROM alpine:3.22
ENV GITDIR=/etc/.pihole
ENV SCRIPTDIR=/opt/pihole
RUN sed -i 's/#\(.*\/community\)/\1/' /etc/apk/repositories
RUN apk --no-cache add bash coreutils curl git jq openrc shadow
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $GITDIR/advanced/Scripts/COL_TABLE $SCRIPTDIR/
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV SKIP_INSTALL=true
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

@@ -1,7 +1,7 @@
FROM quay.io/centos/centos:stream10 FROM quay.io/centos/centos:stream10
# Disable SELinux # Disable SELinux
RUN echo "SELINUX=disabled" > /etc/selinux/config RUN echo "SELINUX=disabled" > /etc/selinux/config
RUN yum install -y --allowerasing curl git RUN yum install -y --allowerasing curl git initscripts
ENV GITDIR=/etc/.pihole ENV GITDIR=/etc/.pihole
ENV SCRIPTDIR=/opt/pihole ENV SCRIPTDIR=/opt/pihole

View File

@@ -1,7 +1,7 @@
FROM quay.io/centos/centos:stream9 FROM quay.io/centos/centos:stream9
# Disable SELinux # Disable SELinux
RUN echo "SELINUX=disabled" > /etc/selinux/config RUN echo "SELINUX=disabled" > /etc/selinux/config
RUN yum install -y --allowerasing curl git RUN yum install -y --allowerasing curl git initscripts
ENV GITDIR=/etc/.pihole ENV GITDIR=/etc/.pihole
ENV SCRIPTDIR=/opt/pihole ENV SCRIPTDIR=/opt/pihole

View File

@@ -0,0 +1,16 @@
FROM buildpack-deps:trixie-scm
ENV GITDIR=/etc/.pihole
ENV SCRIPTDIR=/opt/pihole
RUN mkdir -p $GITDIR $SCRIPTDIR /etc/pihole
ADD . $GITDIR
RUN cp $GITDIR/advanced/Scripts/*.sh $GITDIR/gravity.sh $GITDIR/pihole $GITDIR/automated\ install/*.sh $GITDIR/advanced/Scripts/COL_TABLE $SCRIPTDIR/
ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$SCRIPTDIR
RUN true && \
chmod +x $SCRIPTDIR/*
ENV SKIP_INSTALL=true
#sed '/# Start the installer/Q' /opt/pihole/basic-install.sh > /opt/pihole/stub_basic-install.sh && \

View File

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

View File

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

View File

@@ -1,5 +1,5 @@
FROM fedora:42 FROM fedora:42
RUN dnf install -y git gawk RUN dnf install -y git initscripts
ENV GITDIR=/etc/.pihole ENV GITDIR=/etc/.pihole
ENV SCRIPTDIR=/opt/pihole ENV SCRIPTDIR=/opt/pihole

View File

@@ -1,6 +1,6 @@
pyyaml == 6.0.2 pyyaml == 6.0.3
pytest == 8.3.5 pytest == 8.4.2
pytest-xdist == 3.6.1 pytest-xdist == 3.8.0
pytest-testinfra == 10.2.2 pytest-testinfra == 10.2.2
tox == 4.26.0 tox == 4.31.0
pytest-clarity == 1.0.1 pytest-clarity == 1.0.1

View File

@@ -22,6 +22,7 @@ def test_supported_package_manager(host):
# break supported package managers # break supported package managers
host.run("rm -rf /usr/bin/apt-get") host.run("rm -rf /usr/bin/apt-get")
host.run("rm -rf /usr/bin/rpm") host.run("rm -rf /usr/bin/rpm")
host.run("rm -rf /sbin/apk")
package_manager_detect = host.run( package_manager_detect = host.run(
""" """
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
@@ -66,14 +67,6 @@ def test_installPihole_fresh_install_readableFiles(host):
mock_command("dialog", {"*": ("", "0")}, host) mock_command("dialog", {"*": ("", "0")}, host)
# mock git pull # mock git pull
mock_command_passthrough("git", {"pull": ("", "0")}, host) mock_command_passthrough("git", {"pull": ("", "0")}, host)
# mock PID 1 to pretend to be systemd
mock_command_2(
"ps",
{
"--pid 1": ("systemd", "0"),
},
host,
)
# mock systemctl to not start FTL # mock systemctl to not start FTL
mock_command_2( mock_command_2(
"systemctl", "systemctl",
@@ -81,15 +74,25 @@ def test_installPihole_fresh_install_readableFiles(host):
"enable pihole-FTL": ("", "0"), "enable pihole-FTL": ("", "0"),
"restart pihole-FTL": ("", "0"), "restart pihole-FTL": ("", "0"),
"start pihole-FTL": ("", "0"), "start pihole-FTL": ("", "0"),
"stop pihole-FTL": ("", "0"),
"*": ('echo "systemctl call with $@"', "0"), "*": ('echo "systemctl call with $@"', "0"),
}, },
host, host,
) )
mock_command_2(
"rc-service",
{
"rc-service pihole-FTL enable": ("", "0"),
"rc-service pihole-FTL restart": ("", "0"),
"rc-service pihole-FTL start": ("", "0"),
"*": ('echo "rc-service call with $@"', "0"),
},
host,
)
# try to install man # try to install man
host.run("command -v apt-get > /dev/null && apt-get install -qq man") host.run("command -v apt-get > /dev/null && apt-get install -qq man")
host.run("command -v dnf > /dev/null && dnf install -y man") host.run("command -v dnf > /dev/null && dnf install -y man")
host.run("command -v yum > /dev/null && yum install -y man") host.run("command -v yum > /dev/null && yum install -y man")
host.run("command -v apk > /dev/null && apk add mandoc man-pages")
# Workaround to get FTLv6 installed until it reaches master branch # Workaround to get FTLv6 installed until it reaches master branch
host.run('echo "' + FTL_BRANCH + '" > /etc/pihole/ftlbranch') host.run('echo "' + FTL_BRANCH + '" > /etc/pihole/ftlbranch')
install = host.run( install = host.run(
@@ -98,10 +101,8 @@ def test_installPihole_fresh_install_readableFiles(host):
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
umask 0027 umask 0027
runUnattended=true runUnattended=true
fresh_install=false
source /opt/pihole/basic-install.sh > /dev/null source /opt/pihole/basic-install.sh > /dev/null
runUnattended=true runUnattended=true
fresh_install=false
main main
/opt/pihole/pihole-FTL-prestart.sh /opt/pihole/pihole-FTL-prestart.sh
""" """
@@ -114,7 +115,7 @@ def test_installPihole_fresh_install_readableFiles(host):
maninstalled = False maninstalled = False
piholeuser = "pihole" piholeuser = "pihole"
exit_status_success = 0 exit_status_success = 0
test_cmd = 'su --shell /bin/bash --command "test -{0} {1}" -p {2}' test_cmd = 'su -s /bin/bash -c "test -{0} {1}" -p {2}'
# check files in /etc/pihole for read, write and execute permission # check files in /etc/pihole for read, write and execute permission
check_etc = test_cmd.format("r", "/etc/pihole", piholeuser) check_etc = test_cmd.format("r", "/etc/pihole", piholeuser)
actual_rc = host.run(check_etc).rc actual_rc = host.run(check_etc).rc
@@ -140,6 +141,13 @@ def test_installPihole_fresh_install_readableFiles(host):
check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser) check_macvendor = test_cmd.format("r", "/etc/pihole/macvendor.db", piholeuser)
actual_rc = host.run(check_macvendor).rc actual_rc = host.run(check_macvendor).rc
assert exit_status_success == actual_rc assert exit_status_success == actual_rc
# check readable and executable /etc/init.d/pihole-FTL
check_init = test_cmd.format("x", "/etc/init.d/pihole-FTL", piholeuser)
actual_rc = host.run(check_init).rc
assert exit_status_success == actual_rc
check_init = test_cmd.format("r", "/etc/init.d/pihole-FTL", piholeuser)
actual_rc = host.run(check_init).rc
assert exit_status_success == actual_rc
# check readable and executable manpages # check readable and executable manpages
if maninstalled is True: if maninstalled is True:
check_man = test_cmd.format("x", "/usr/local/share/man", piholeuser) check_man = test_cmd.format("x", "/usr/local/share/man", piholeuser)
@@ -475,6 +483,7 @@ def test_package_manager_has_pihole_deps(host):
""" """
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
update_package_cache
build_dependency_package build_dependency_package
install_dependent_packages install_dependent_packages
""" """
@@ -491,6 +500,7 @@ def test_meta_package_uninstall(host):
""" """
source /opt/pihole/basic-install.sh source /opt/pihole/basic-install.sh
package_manager_detect package_manager_detect
update_package_cache
build_dependency_package build_dependency_package
install_dependent_packages install_dependent_packages
""" """

10
test/tox.alpine_3_21.ini Normal file
View File

@@ -0,0 +1,10 @@
[tox]
envlist = py3
[testenv:py3]
allowlist_externals = docker
deps = -rrequirements.txt
setenv =
COLUMNS=120
commands = docker buildx build --load --progress plain -f _alpine_3_21.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py

10
test/tox.alpine_3_22.ini Normal file
View File

@@ -0,0 +1,10 @@
[tox]
envlist = py3
[testenv:py3]
allowlist_externals = docker
deps = -rrequirements.txt
setenv =
COLUMNS=120
commands = docker buildx build --load --progress plain -f _alpine_3_22.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py

10
test/tox.debian_13.ini Normal file
View File

@@ -0,0 +1,10 @@
[tox]
envlist = py3
[testenv:py3]
allowlist_externals = docker
deps = -rrequirements.txt
setenv =
COLUMNS=120
commands = docker buildx build --load --progress plain -f _debian_13.Dockerfile -t pytest_pihole:test_container ../
pytest {posargs:-vv -n auto} ./test_any_automated_install.py ./test_any_utils.py