From ecde2225122904a89543712ec8f2b0fd1da26ff6 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Tue, 20 Jun 2017 22:17:41 +0100 Subject: [PATCH 01/28] [Staging] 3.1 (#1502) * Fix handling of wildcard help text * Rewrite help text for better handling of params * Replace misleading letter variable * stash changes on branch switch, else it fails if any changes have been made. * Make changes according to comment in #1384 * Update queryFunc() * Allow scanList() to search files using a wildcard by removing quotes wrapped around `${list}` * scanList() will not provide a domain ouput on each string if exact is specified (`grep -l`) * Remove unused processWildcards() function * Return a message if no domain is specified * IDN domains are converted to punycode when running a `pihole -q` search if the `python` package is available, otherwise will revert to current behaviour * Scan Blacklist & Wildcards first, exiting from search if a match is found (Fixes #1330) * Use one `grep` subshell to search for all "*.domains" lists at once (opposed to looping to get every matching file name, and then spawning a `grep` instance for every matching file) * queryFunc() will not return "(0 results)" output from files where no match is found * Sort results based off list number * Return a message if no results are found * Update basic-install.sh * Update block page. Allow for setupVars setting of CUSTOMBLOCKPAGE (bool) to prevent it being overwritten * simplify * further simplify * fix inteliJ IDEA complaints * even further simplify * tidy up output * revert line, looks tidyer * clarify * Revert "Ensure any changes to blocking page are updated." * We test for dpkg lock on line 830 directly, no need for the check also in the template section. Signed-off-by: Dan Schaper * Display FTL version & version.sh rewrite While testing to make sure `pihole -v` would output `pihole-FTL version`, I noticed some options didn't work how I expected them to. For example, if I use `pihole -v -p`, I would expect to see the version output of Pi-hole Core. Instead, I'm informed that it's an invalid option. I've had the following things in mind while rewriting this: * I'm operating under the assumption that FTL is only installed if the Admin Console is (Line 113 exit 0) * I have modified the help text to only output with `pihole -v --help` * I have modified all output to be more similar to the output style of `grep` and `curl` (Ditching ":::") Testing output: ``` w3k@MCT:~$ pihole -v Pi-hole version is v3.0.1-14-ga928cd3 (Latest: v3.0.1) Admin Console version is v3.0-9-g3760482 (Latest: v3.0.1) FTL version is v2.6.2 (Latest: v2.6.2) w3k@MCT:~$ pihole -v -c Current Pi-hole version is v3.0.1-14-ga928cd3 Current Admin Console version is v3.0-9-g3760482 Current FTL version is v2.6.2 w3k@MCT:~$ pihole -v -l Latest Pi-hole version is v3.0.1 Latest Admin Console version is v3.0.1 Latest FTL version is v2.6.2 w3k@MCT:~$ pihole -v -p --hash Current Pi-hole hash is a928cd3 w3k@MCT:~$ pihole -v -a --hash Current Admin Console hash is 3760482 w3k@MCT:~$ pihole -v --help Usage: pihole -v [REPO | OPTION] [OPTION] Show Pi-hole, Web Admin & FTL versions w3k@MCT:~$ pihole -v -foo Invalid Option! ``` * Update -h to work as --hash Also provide error output as per https://github.com/pi-hole/pi-hole/pull/1447#issuecomment-300600093 * Perform EXACT searches on HOSTS lists correctly `\s` on the end may be overkill, but it is the existing scanList() behaviour. * Fixed indentation * Minimise string duplication & other minor changes Instead of duplicating output strings, rewrite core/web/ftlOutput() into one neat versionOutput(). * Modified syntax to be valid for Shellcheck * Log and echo gateway responses * Update queryFunc() to search Whitelist If there is a match in Whitelist/Blacklist/Wildcards, `[ ! -t 1 ]` will cause the search to end if the terminal is closed when the script is called. This has the intended effect of allowing a user to search for a W/B/W domain (as well as all the adlists it's found in) using `pihole -q` via Terminal, but the script will stop searching after a W/B/W match when called by the block page. * Wrap in double brackets * Provide remote hashes for version.sh * Provide remote hashes for comparison * Use double braces for all conditions (for consistency) * Suppress potential "cd" error output * Provide "not applicable" output upon any hash request for FTL * whitelist on website blocked doesnt work (#1452) Since Pi-hole redirects ad domains to itself, accessing the script via de.ign.com is the same as pi.hole in this case. The fix should be as simple as adding a / before admin on this line. * Solve piholeLogFlush.sh having to be issued 2 x to clear logs (#1460) Simplified the command -v syntax, and added a sleep 3 timer to the first execution of the log rotation. The second execution was being issued while the first was still running, thus it would fail and you would have to issue the "Flush Logs" command a second time. * Use `echo "ABC" | pihole tricorder` to upload to Pi-hole's medical tricorder. Uses SSL if available. * Update list.sh I believe this has feature parity with `sed /foo/ Id` but also supports busybox, and my alpine docker ;) * Document `sed` substitution for user readability Comment the oneliner with explanations of what each step does. * Update Help Output (#1467) * File consistency * Tabs to 2 spaces * Corrected indenting * Double braced conditionals * Quoted variables within conditionals * Standardise core help text * Added help text for disable command * Added help text for logging command * Clean up * Fixed certain new lines and spaces * Sync with development branch * Formatting consistency * Tabs to 2 spaces * Corrected indenting * Double braced conditionals * Quoted variables within conditionals * Fixed certain newlines and spaces * Admin help text * Added help text for interface command * Sync with development branch * Formatting consistency * Tabs to 2 spaces * Fixed some wording * Fixed certain spaces * Formatting consistency * Minor wording changes * Tabs to 2 spaces * Corrected indenting * Double braced conditionals * Quoted variables within conditionals * Fixed certain newlines and spaces * Blacklist help text * Formatting consistency * Tabs to 2 spaces * Corrected indenting * Cronometer help text * Formatting consistency * Fixed certain newlines and spaces * Corrected indenting * Checkout warning alteration * Add checkout help text * Corrected help output * Show help for "pihole -a -i --help" * Fix "pihole disable --help" and "pihole -l --help" * Show help for "pihole -v -h" * Indent output text * Minor help text change * Show help for "pihole checkout --help" * Tricorder: Insecure Opt-out * Check to see if Tricorder is being called directly * Provide opt-out for insecure transmission of debug log * Remove mention of internal function from help menu * :taco: is the new :shipit: squirrel * Wording changes and bug fix * Fix wildcard help text * -wild is not a valid option since we're already using -wild * Fix logrotation: manual flushing should be done twice, but automated rotation at midnight should only be done *once*! * Print echos only when manual flushing is requested * Add "quiet" mode + update comments in the cron file * Confirm Tricorder is online * Scan port 9998 to confirm the availability of "tricorder.pi-hole.net" * Exit codes for upload process * Formatting consistency * Add link to Windows DNS Swapper See #1400 * Install loopback firewall rules for FTL (#1419) * Install loopback firewall rules for FTL * FirewallD FTL ports Signed-off-by: Dan Schaper * Remove firewallD FTL local rules. Local rules should not be blocked in firewallD, not requred for internal service FTD> * Reinstate https rules, and delete FTL rules Fixes earlier commit. * Retrieve local repos on repair (#1481) * Retrieve local repos on repair * Change conditional to check for repair * Change wording of Update/Reconfigure message * Fixed indenting * Perform "git reset --hard" on reconfigure * Change directory before trying to reset repository. Fixes #1489 * No need to `cd $PWD` as it doesn't affect flow of caller script. Signed-off-by: Dan Schaper * Refine output of password status in basic-install.sh:displayFinalMessage(). Fixes #1488 (#1490) * Rewrite Chronometer to output more stats * Fix output IPv4 addr when removing CIDR notation (#1498) * Move wildcards file if blocking is disabled (#1495) * Move wildcards file if blocking is diabled * Delete newline * Roll back merge #1417 (#1494) * Update ISSUE_TEMPLATE.md * Remove Question option * Prefer ULA over GUA addresses [IPv6] (#1508) * On installs with GUA and ULA's we should prefer ULA's as it's been demonstrated that GUA's can and often are rotated by ISPs. Fixes #1473 * Add test for link-local address detection * Add ULA-only and GUA-only tests * Add test_IPv6_GUA_ULA_test and test_IPv6_ULA_GUA_test * Add "" * Add mock_command_2 command that can mock a command with more than one argument (as "ip -6 address") and result multiple lines of results * Make mock_command_2 more similar to the original mock_command * Correct comments * Fixed remaining comments * Fixed one last comment... * Fixed a comment... * Add weekly logrotation of FTL's log (#1509) * Update LICENSE of the project to EUPL v1.2 * Make clear that NO is the default if the user just hits return (#1514) * Add tricorderFunc back as usable function (#1515) As per #1464 * Don't update FTL when there is a core update (as this will update FTL a second time). Fixes #1516 * Add FTL tests to the test suite (#1510) * Add first version of FTL tests * Wait one second to allow FTL to start up and analyze our mock log * Add test_FTL_telnet_statistics * Added test_FTL_telnet_top_clients * Add test_FTL_telnet_top_domains * Revert "Add FTL tests to the test suite (#1510)" (#1519) This reverts commit cf6a1ac9adb4e26570cc5da7c8be628080f37e0f. * Trim version output when update is successful (#1527) * Change ownership of /etc/pihole to user/group pihole. Fixes #1529 (#1530) * Delete temporary files after installing the FTL binary. Fixes #1525 * Change from admin to approvers teams * Introduce new file black.list for blacklist content * Add "pihole -g -b" to *only* update black.list (saves a bunch of time when adding/changing only blacklisted files - won'tdownload lal lists, but only processes the blacklist and restars dnsmasq) * Remove useless cat * Improve displayed messages and overall logic * Disable black.list on "pihole disable" * cp + rm === mv (well, almost) --- .github/ISSUE_TEMPLATE.md | 2 +- .pullapprove.yml | 4 +- LICENSE | 119 +++---- README.md | 1 + advanced/01-pihole.conf | 1 + advanced/Scripts/chronometer.sh | 479 ++++++++++++++++++++++++----- advanced/Scripts/list.sh | 311 +++++++++---------- advanced/Scripts/piholeCheckout.sh | 32 +- advanced/Scripts/piholeDebug.sh | 8 +- advanced/Scripts/piholeLogFlush.sh | 43 ++- advanced/Scripts/update.sh | 13 +- advanced/Scripts/version.sh | 165 ++++++---- advanced/Scripts/webpage.sh | 187 +++++------ advanced/index.php | 2 +- advanced/logrotate | 11 + advanced/pihole-FTL.service | 2 +- advanced/pihole.cron | 7 +- automated install/basic-install.sh | 98 ++++-- gravity.sh | 109 ++++--- pihole | 207 +++++++++---- test/test_automated_install.py | 76 +++++ 21 files changed, 1227 insertions(+), 650 deletions(-) diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 3014625b..23e67795 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -9,7 +9,7 @@ _{replace this text with a number from 1 to 10, with 1 being not familiar, and 10 being very familiar}_ --- -**[FEATURE REQUEST | QUESTION | OTHER]:** +**[BUG REPORT | OTHER]:** Please [submit your feature request here](https://discourse.pi-hole.net/c/feature-requests), so it is votable by the community. It's also easier for us to track. diff --git a/.pullapprove.yml b/.pullapprove.yml index 39566b34..30888234 100644 --- a/.pullapprove.yml +++ b/.pullapprove.yml @@ -10,7 +10,7 @@ group_defaults: reset_on_push: enabled: true reject_value: -2 - approve_regex: '^(Approved|:shipit:|:\+1:|Engage)' + approve_regex: '^(Approved|:shipit:|:\+1:|Engage|:taco:)' reject_regex: '^(Rejected|:-1:|Borg)' author_approval: auto: true @@ -35,4 +35,4 @@ groups: - master required: 4 teams: - - admin + - approvers diff --git a/LICENSE b/LICENSE index 9fddaad1..9ce6e5b0 100644 --- a/LICENSE +++ b/LICENSE @@ -12,81 +12,63 @@ This license applies to the whole project EXCEPT: The licenses that existed prior to this change have remained intact. ------------------------------------------------------------- +EUROPEAN UNION PUBLIC LICENCE v. 1.2 -European Union Public Licence -V. 1.1 +EUPL Β© the European Union 2007, 2016 -EUPL (C) the European Community 2007 - -This European Union Public Licence (the "EUPL") applies to the Work or Software (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work). - -The Original Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Original Work: - -Licensed under the EUPL V.1.1 - -or has expressed by any other mean his willingness to license under the EUPL. +This European Union Public Licence (the EUPL) applies to the Work (as defined below) which is provided under the terms of this Licence. Any use of the Work, other than as authorised under this Licence is prohibited (to the extent such use is covered by a right of the copyright holder of the Work). +The Work is provided under the terms of this Licence when the Licensor (as defined below) has placed the following notice immediately following the copyright notice for the Work: +Licensed under the EUPL +or has expressed by any other means his willingness to license under the EUPL. 1. Definitions In this Licence, the following terms have the following meaning: - The Licence: this Licence. - -- The Original Work or the Software: the software distributed and/or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be. - +- The Original Work: the work or software distributed or communicated by the Licensor under this Licence, available as Source Code and also as Executable Code as the case may be. - Derivative Works: the works or software that could be created by the Licensee, based upon the Original Work or modifications thereof. This Licence does not define the extent of modification or dependence on the Original Work required in order to classify a work as a Derivative Work; this extent is determined by copyright law applicable in the country mentioned in Article 15. - -- The Work: the Original Work and/or its Derivative Works. - +- The Work: the Original Work or its Derivative Works. - The Source Code: the human-readable form of the Work which is the most convenient for people to study and modify. - - The Executable Code: any code which has generally been compiled and which is meant to be interpreted by a computer as a program. - -- The Licensor: the natural or legal person that distributes and/or communicates the Work under the Licence. - +- The Licensor: the natural or legal person that distributes or communicates the Work under the Licence. - Contributor(s): any natural or legal person who modifies the Work under the Licence, or otherwise contributes to the creation of a Derivative Work. - -- The Licensee or "You": any natural or legal person who makes any usage of the Software under the terms of the Licence. - -- Distribution and/or Communication: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, on-line or off-line, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person. +- The Licensee or You: any natural or legal person who makes any usage of the Work under the terms of the Licence. +- Distribution or Communication: any act of selling, giving, lending, renting, distributing, communicating, transmitting, or otherwise making available, online or offline, copies of the Work or providing access to its essential functionalities at the disposal of any other natural or legal person. 2. Scope of the rights granted by the Licence -The Licensor hereby grants You a world-wide, royalty-free, non-exclusive, sub-licensable licence to do the following, for the duration of copyright vested in the Original Work: - +The Licensor hereby grants You a worldwide, royalty-free, non-exclusive, sublicensable licence to do the following, for the duration of copyright vested in the Original Work: - use the Work in any circumstance and for all usage, - reproduce the Work, -- modify the Original Work, and make Derivative Works based upon the Work, +- modify the Work, and make Derivative Works based upon the Work, - communicate to the public, including the right to make available or display the Work or copies thereof to the public and perform publicly, as the case may be, the Work, - distribute the Work or copies thereof, - lend and rent the Work or copies thereof, -- sub-license rights in the Work or copies thereof. - +- sublicense rights in the Work or copies thereof. Those rights can be exercised on any media, supports and formats, whether now known or later invented, as far as the applicable law permits so. - In the countries where moral rights apply, the Licensor waives his right to exercise his moral right to the extent allowed by law in order to make effective the licence of the economic rights here above listed. - -The Licensor grants to the Licensee royalty-free, non exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence. +The Licensor grants to the Licensee royalty-free, non-exclusive usage rights to any patents held by the Licensor, to the extent necessary to make use of the rights granted on the Work under this Licence. 3. Communication of the Source Code -The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute and/or communicate the Work. +The Licensor may provide the Work either in its Source Code form, or as Executable Code. If the Work is provided as Executable Code, the Licensor provides in addition a machine-readable copy of the Source Code of the Work along with each copy of the Work that the Licensor distributes or indicates, in a notice following the copyright notice attached to the Work, a repository where the Source Code is easily and freely accessible for as long as the Licensor continues to distribute or communicate the Work. 4. Limitations on copyright -Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Original Work or Software, of the exhaustion of those rights or of other applicable limitations thereto. +Nothing in this Licence is intended to deprive the Licensee of the benefits from any exception or limitation to the exclusive rights of the rights owners in the Work, of the exhaustion of those rights or of other applicable limitations thereto. 5. Obligations of the Licensee The grant of the rights mentioned above is subject to some restrictions and obligations imposed on the Licensee. Those obligations are the following: -Attribution right: the Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes and/or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification. +Attribution right: The Licensee shall keep intact all copyright, patent or trademarks notices and all notices that refer to the Licence and to the disclaimer of warranties. The Licensee must include a copy of such notices and a copy of the Licence with every copy of the Work he/she distributes or communicates. The Licensee must cause any Derivative Work to carry prominent notices stating that the Work has been modified and the date of modification. -Copyleft clause: If the Licensee distributes and/or communicates copies of the Original Works or Derivative Works based upon the Original Work, this Distribution and/or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence. +Copyleft clause: If the Licensee distributes or communicates copies of the Original Works or Derivative Works, this Distribution or Communication will be done under the terms of this Licence or of a later version of this Licence unless the Original Work is expressly distributed only under this version of the Licence - for example by communicating EUPL v. 1.2 only. The Licensee (becoming Licensor) cannot offer or impose any additional terms or conditions on the Work or Derivative Work that alter or restrict the terms of the Licence. -Compatibility clause: If the Licensee Distributes and/or Communicates Derivative Works or copies thereof based upon both the Original Work and another work licensed under a Compatible Licence, this Distribution and/or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, "Compatible Licence" refers to the licences listed in the appendix attached to this Licence. Should the Licensee’s obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. +Compatibility clause: If the Licensee Distributes or Communicates Derivative Works or copies thereof based upon both the Work and another work licensed under a Compatible Licence, this Distribution or Communication can be done under the terms of this Compatible Licence. For the sake of this clause, Compatible Licence refers to the licences listed in the appendix attached to this Licence. Should the Licensee's obligations under the Compatible Licence conflict with his/her obligations under this Licence, the obligations of the Compatible Licence shall prevail. -Provision of Source Code: When distributing and/or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute and/or communicate the Work. +Provision of Source Code: When distributing or communicating copies of the Work, the Licensee will provide a machine-readable copy of the Source Code or indicate a repository where this Source will be easily and freely available for as long as the Licensee continues to distribute or communicate the Work. Legal Protection: This Licence does not grant permission to use the trade names, trademarks, service marks, or names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the copyright notice. @@ -100,10 +82,8 @@ Each time You accept the Licence, the original Licensor and subsequent Contribut 7. Disclaimer of Warranty -The Work is a work in progress, which is continuously improved by numerous contributors. It is not a finished work and may therefore contain defects or "bugs" inherent to this type of software development. - -For the above reason, the Work is provided under the Licence on an "as is" basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence. - +The Work is a work in progress, which is continuously improved by numerous Contributors. It is not a finished work and may therefore contain defects or bugs inherent to this type of development. +For the above reason, the Work is provided under the Licence on an as is basis and without warranties of any kind concerning the Work, including without limitation merchantability, fitness for a particular purpose, absence of defects or errors, accuracy, non-infringement of intellectual property rights other than copyright as stated in Article 6 of this Licence. This disclaimer of warranty is an essential part of the Licence and a condition for the grant of any rights to the Work. 8. Disclaimer of Liability @@ -112,56 +92,55 @@ Except in the cases of wilful misconduct or damages directly caused to natural p 9. Additional agreements -While distributing the Original Work or Derivative Works, You may choose to conclude an additional agreement to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or services consistent with this Licence. However, in accepting such obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any such warranty or additional liability. +While distributing the Work, You may choose to conclude an additional agreement, defining obligations or services consistent with this Licence. However, if accepting obligations, You may act only on your own behalf and on your sole responsibility, not on behalf of the original Licensor or any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against such Contributor by the fact You have accepted any warranty or additional liability. 10. Acceptance of the Licence -The provisions of this Licence can be accepted by clicking on an icon "I agree" placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions. - -Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution and/or Communication by You of the Work or copies thereof. +The provisions of this Licence can be accepted by clicking on an icon I agree placed under the bottom of a window displaying the text of this Licence or by affirming consent in any other similar way, in accordance with the rules of applicable law. Clicking on that icon indicates your clear and irrevocable acceptance of this Licence and all of its terms and conditions. +Similarly, you irrevocably accept this Licence and all of its terms and conditions by exercising any rights granted to You by Article 2 of this Licence, such as the use of the Work, the creation by You of a Derivative Work or the Distribution or Communication by You of the Work or copies thereof. 11. Information to the public -In case of any Distribution and/or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee. +In case of any Distribution or Communication of the Work by means of electronic communication by You (for example, by offering to download the Work from a remote location) the distribution channel or media (for example, a website) must at least provide to the public the information requested by the applicable law regarding the Licensor, the Licence and the way it may be accessible, concluded, stored and reproduced by the Licensee. 12. Termination of the Licence The Licence and the rights granted hereunder will terminate automatically upon any breach by the Licensee of the terms of the Licence. - Such a termination will not terminate the licences of any person who has received the Work from the Licensee under the Licence, provided such persons remain in full compliance with the Licence. 13. Miscellaneous -Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work licensed hereunder. - -If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed and/or reformed so as necessary to make it valid and enforceable. - -The European Commission may publish other linguistic versions and/or new versions of this Licence, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number. - +Without prejudice of Article 9 above, the Licence represents the complete agreement between the Parties as to the Work. +If any provision of the Licence is invalid or unenforceable under applicable law, this will not affect the validity or enforceability of the Licence as a whole. Such provision will be construed or reformed so as necessary to make it valid and enforceable. +The European Commission may publish other linguistic versions or new versions of this Licence or updated versions of the Appendix, so far this is required and reasonable, without reducing the scope of the rights granted by the Licence. New versions of the Licence will be published with a unique version number. All linguistic versions of this Licence, approved by the European Commission, have identical value. Parties can take advantage of the linguistic version of their choice. 14. Jurisdiction -Any litigation resulting from the interpretation of this License, arising between the European Commission, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Communities, as laid down in article 238 of the Treaty establishing the European Community. - -Any litigation arising between Parties, other than the European Commission, and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. +Without prejudice to specific agreement between parties, +- any litigation resulting from the interpretation of this License, arising between the European Union institutions, bodies, offices or agencies, as a Licensor, and any Licensee, will be subject to the jurisdiction of the Court of Justice of the European Union, as laid down in article 272 of the Treaty on the Functioning of the European Union, +- any litigation arising between other parties and resulting from the interpretation of this License, will be subject to the exclusive jurisdiction of the competent court where the Licensor resides or conducts its primary business. 15. Applicable Law -This Licence shall be governed by the law of the European Union country where the Licensor resides or has his registered office. - -This licence shall be governed by the Belgian law if: - -- a litigation arises between the European Commission, as a Licensor, and any Licensee; -- the Licensor, other than the European Commission, has no residence or registered office inside a European Union country. - +Without prejudice to specific agreement between parties, +- this Licence shall be governed by the law of the European Union Member State where the Licensor has his seat, resides or has his registered office, +- this licence shall be governed by Belgian law if the Licensor has no seat, residence or registered office inside a European Union Member State. === + Appendix -"Compatible Licences" according to article 5 EUPL are: -- GNU General Public License (GNU GPL) v. 2 +Compatible Licences according to Article 5 EUPL are: +- GNU General Public License (GPL) v. 2, v. 3 +- GNU Affero General Public License (AGPL) v. 3 - Open Software License (OSL) v. 2.1, v. 3.0 -- Common Public License v. 1.0 -- Eclipse Public License v. 1.0 -- Cecill v. 2.0 +- Eclipse Public License (EPL) v. 1.0 +- CeCILL v. 2.0, v. 2.1 +- Mozilla Public Licence (MPL) v. 2 +- GNU Lesser General Public Licence (LGPL) v. 2.1, v. 3 +- Creative Commons Attribution-ShareAlike v. 3.0 Unported (CC BY-SA 3.0) for works other than software +- European Union Public Licence (EUPL) v. 1.1, v. 1.2 +- QuΓ©bec Free and Open-Source Licence - Reciprocity (LiLiQ-R) or Strong Reciprocity (LiLiQ-R+) +- The European Commission may update this Appendix to later versions of the above licences without producing a new version of the EUPL, as long as they provide the rights granted in Article 2 of this Licence and protect the covered Source Code from exclusive appropriation. +- All other changes or additions to this Appendix require the production of a new EUPL version. diff --git a/README.md b/README.md index 75c548e5..6f8813fa 100644 --- a/README.md +++ b/README.md @@ -156,6 +156,7 @@ You can view [real-time stats](https://discourse.pi-hole.net/t/how-do-i-view-my- - [Let your blink1 device blink when Pi-hole filters ads](https://gist.github.com/elpatron68/ec0b4c582e5abf604885ac1e068d233f) - [Pi-hole Prometheus exporter](https://github.com/nlamirault/pihole_exporter): a [Prometheus](https://prometheus.io/) exporter for Pi-hole - [Pi-hole Droid - open source Android client](https://github.com/friimaind/pi-hole-droid) +- [Windows DNS Swapper](https://github.com/roots84/DNS-Swapper), see [#1400](https://github.com/pi-hole/pi-hole/issues/1400) ## Coverage diff --git a/advanced/01-pihole.conf b/advanced/01-pihole.conf index 1b157f88..79735c15 100644 --- a/advanced/01-pihole.conf +++ b/advanced/01-pihole.conf @@ -22,6 +22,7 @@ addn-hosts=/etc/pihole/gravity.list addn-hosts=/etc/pihole/local.list +addn-hosts=/etc/pihole/black.list domain-needed diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index 67ff495b..d9b7d05b 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -8,101 +8,428 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. -#Functions############################################################################################################## -piLog="/var/log/pihole.log" -gravity="/etc/pihole/gravity.list" - -. /etc/pihole/setupVars.conf - -function GetFTLData { +# Retrieve stats from FTL engine +pihole-FTL() { + ftl_port=$(cat /var/run/pihole-FTL.port 2> /dev/null) + if [[ -n "$ftl_port" ]]; then # Open connection to FTL - exec 3<>/dev/tcp/localhost/"$(cat /var/run/pihole-FTL.port)" + exec 3<>"/dev/tcp/localhost/$ftl_port" # Test if connection is open - if { >&3; } 2> /dev/null; then - # Send command to FTL - echo -e ">$1" >&3 + if { "true" >&3; } 2> /dev/null; then + # Send command to FTL + echo -e ">$1" >&3 - # Read input - read -r -t 1 LINE <&3 - until [ ! $? ] || [[ "$LINE" == *"EOM"* ]]; do - echo "$LINE" >&1 - read -r -t 1 LINE <&3 - done + # Read input + read -r -t 1 LINE <&3 + until [[ ! $? ]] || [[ "$LINE" == *"EOM"* ]]; do + echo "$LINE" >&1 + read -r -t 1 LINE <&3 + done - # Close connection - exec 3>&- - exec 3<&- + # Close connection + exec 3>&- + exec 3<&- fi + else + echo -e "${COL_LIGHT_RED}FTL offline${COL_NC}" + fi } -outputJSON() { - get_summary_data - echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw}}" +# Print spaces to align right-side content +printFunc() { + txt_len="${#2}" + + # Reduce string length when using colour code + [ "${2:0:1}" == "" ] && txt_len=$((txt_len-7)) + + if [[ "$3" == "last" ]]; then + # Prevent final line from printing trailing newline + scr_size=( $(stty size 2>/dev/null || echo 24 80) ) + scr_width="${scr_size[1]}" + + title_len="${#1}" + spc_num=$(( (scr_width - title_len) - txt_len )) + [[ "$spc_num" -lt 0 ]] && spc_num="0" + spc=$(printf "%${spc_num}s") + + printf "%s%s$spc" "$1" "$2" + else + # Determine number of spaces for padding + spc_num=$(( 20 - txt_len )) + [[ "$spc_num" -lt 0 ]] && spc_num="0" + spc=$(printf "%${spc_num}s") + + # Print string (Max 20 characters, prevents overflow) + printf "%s%s$spc" "$1" "${2:0:20}" + fi } -get_summary_data() { - local summary=$(GetFTLData "stats") - domains_being_blocked_raw=$(grep "domains_being_blocked" <<< "${summary}" | grep -Eo "[0-9]+$") - domains_being_blocked=$(printf "%'.f" ${domains_being_blocked_raw}) - dns_queries_today_raw=$(grep "dns_queries_today" <<< "$summary" | grep -Eo "[0-9]+$") - dns_queries_today=$(printf "%'.f" ${dns_queries_today_raw}) - ads_blocked_today_raw=$(grep "ads_blocked_today" <<< "$summary" | grep -Eo "[0-9]+$") - ads_blocked_today=$(printf "%'.f" ${ads_blocked_today_raw}) - ads_percentage_today_raw=$(grep "ads_percentage_today" <<< "$summary" | grep -Eo "[0-9.]+$") - LC_NUMERIC=C ads_percentage_today=$(printf "%'.f" ${ads_percentage_today_raw}) +# Perform on first Chrono run (not for JSON formatted string) +get_init_stats() { + LC_NUMERIC=C + calcFunc(){ awk "BEGIN {print $*}"; } + + # Convert bytes to human-readable format + hrBytes() { + awk '{ + num=$1; + if(num==0) { + print "0 B" + } else { + xxx=(num<0?-num:num) + sss=(num<0?-1:1) + split("B KB MB GB TB PB",type) + for(i=5;yyy < 1;i--) { + yyy=xxx / (2^(10*i)) + } + printf "%.0f " type[i+2], yyy*sss + } + }' <<< "$1"; + } + + # Convert seconds to human-readable format + hrSecs() { + day=$(( $1/60/60/24 )); hrs=$(( $1/3600%24 )); mins=$(( ($1%3600)/60 )); secs=$(( $1%60 )) + [[ "$day" -ge "2" ]] && plu="s" + [[ "$day" -ge "1" ]] && days="$day day${plu}, " || days="" + printf "%s%02d:%02d:%02d\n" "$days" "$hrs" "$mins" "$secs" + } + + # Set Colour Codes + coltable="/opt/pihole/COL_TABLE" + if [[ -f "${coltable}" ]]; then + source ${coltable} + else + COL_NC='' + COL_DARK_GRAY='' + COL_LIGHT_GREEN='' + COL_LIGHT_BLUE='' + COL_LIGHT_RED='' + COL_YELLOW='' + COL_LIGHT_RED='' + COL_URG_RED='' + fi + + # Get RPi model number, or OS distro info + if command -v vcgencmd &> /dev/null; then + sys_rev=$(awk '/Revision/ {print $3}' < /proc/cpuinfo) + case "$sys_rev" in + 000[2-6]) sys_model=" 1, Model B";; # 256MB + 000[7-9]) sys_model=" 1, Model A" ;; # 256MB + 000d|000e|000f) sys_model=" 1, Model B";; # 512MB + 0010|0013) sys_model=" 1, Model B+";; # 512MB + 0012|0015) sys_model=" 1, Model A+";; # 256MB + a0104[0-1]|a21041|a22042) sys_model=" 2, Model B";; # 1GB + 900021) sys_model=" 1, Model A+";; # 512MB + 900032) sys_model=" 1, Model B+";; # 512MB + 90009[2-3]|920093) sys_model=" Zero";; # 512MB + 9000c1) sys_model=" Zero W";; # 512MB + a02082|a[2-3]2082) sys_model=" 3, Model B";; # 1GB + *) sys_model="" ;; + esac + sys_type="Raspberry Pi$sys_model" + else + source "/etc/os-release" + CODENAME=$(sed 's/[()]//g' <<< "${VERSION/* /}") + sys_type="${NAME/ */} ${CODENAME^} $VERSION_ID" + fi + + # Get core count + sys_cores=$(grep -c "^processor" /proc/cpuinfo) + [[ "$sys_cores" -ne 1 ]] && sys_cores_plu="cores" || sys_cores_plu="core" + + # Test existence of clock speed file for ARM CPU + if [[ -f "/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" ]]; then + scaling_freq_file="/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq" + fi + + # Test existence of temperature file + if [[ -f "/sys/class/thermal/thermal_zone0/temp" ]]; then + temp_file="/sys/class/thermal/thermal_zone0/temp" + elif [[ -f "/sys/class/hwmon/hwmon0/temp1_input" ]]; then + temp_file="/sys/class/hwmon/hwmon0/temp1_input" + else + temp_file="" + fi + + # Test existence of setupVars config + if [[ -f "/etc/pihole/setupVars.conf" ]]; then + setupVars="/etc/pihole/setupVars.conf" + fi } -normalChrono() { - for (( ; ; )); do - get_summary_data - domain=$(GetFTLData recentBlocked) - clear - # Displays a colorful Pi-hole logo - echo " ___ _ _ _" - echo "| _ (_)___| |_ ___| |___" - echo "| _/ |___| ' \/ _ \ / -_)" - echo "|_| |_| |_||_\___/_\___|" - echo "" - echo " ${IPV4_ADDRESS}" - echo "" - uptime | cut -d' ' -f11- - #uptime -p #Doesn't work on all versions of uptime - uptime | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/) {d=$6;h=$8;m=$9} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes."}' - echo "-------------------------------" - echo "Recently blocked:" - echo " $domain" +get_sys_stats() { + local ph_ver_raw + local cpu_raw + local ram_raw + local disk_raw - echo "Blocking: ${domains_being_blocked}" - echo "Queries: ${dns_queries_today}" - echo "Pi-holed: ${ads_blocked_today} (${ads_percentage_today}%)" - - sleep 5 - done + # Update every 12 refreshes (Def: every 60s) + count=$((count+1)) + if [[ "$count" == "1" ]] || (( "$count" % 12 == 0 )); then + [[ -n "$setupVars" ]] && source "$setupVars" + + + ph_ver_raw=($(pihole -v -c 2> /dev/null | sed -n 's/^.* v/v/p')) + if [[ -n "${ph_ver_raw[0]}" ]]; then + ph_core_ver="${ph_ver_raw[0]}" + ph_lte_ver="${ph_ver_raw[1]}" + ph_ftl_ver="${ph_ver_raw[2]}" + else + ph_core_ver="${COL_LIGHT_RED}API unavailable${COL_NC}" + fi + + sys_name=$(hostname) + + [[ -n "$TEMPERATUREUNIT" ]] && temp_unit="$TEMPERATUREUNIT" || temp_unit="c" + + # Get storage stats for partition mounted on / + disk_raw=($(df -B1 / 2> /dev/null | awk 'END{ print $3,$2,$5 }')) + disk_used="${disk_raw[0]}" + disk_total="${disk_raw[1]}" + disk_perc="${disk_raw[2]}" + + net_gateway=$(route -n | awk '$4 == "UG" {print $2;exit}') + + # Get DHCP stats, if feature is enabled + if [[ "$DHCP_ACTIVE" == "true" ]]; then + ph_dhcp_eip="${DHCP_END##*.}" + ph_dhcp_max=$(( ${DHCP_END##*.} - ${DHCP_START##*.} + 1 )) + fi + + # Get alt DNS server, or print total count of alt DNS servers + if [[ -z "${PIHOLE_DNS_3}" ]]; then + ph_alts="${PIHOLE_DNS_2}" + else + dns_count="0" + [[ -n "${PIHOLE_DNS_2}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_3}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_4}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_5}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_6}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_7}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_8}" ]] && dns_count=$((dns_count+1)) + [[ -n "${PIHOLE_DNS_9}" ]] && dns_count="$dns_count+" + ph_alts="${dns_count} others" + fi + fi + + sys_uptime=$(hrSecs "$(cut -d. -f1 /proc/uptime)") + sys_loadavg=$(cut -d " " -f1,2,3 /proc/loadavg) + + # Get CPU usage, only counting processes over 1% CPU as active + cpu_raw=$(ps -eo pcpu,rss --no-headers | grep -E -v " 0") + cpu_tasks=$(wc -l <<< "$cpu_raw") + cpu_taskact=$(sed -r "/(^ 0.)/d" <<< "$cpu_raw" | wc -l) + cpu_perc=$(awk '{sum+=$1} END {printf "%.0f\n", sum/'"$sys_cores"'}' <<< "$cpu_raw") + + # Get CPU clock speed + if [[ -n "$scaling_freq_file" ]]; then + cpu_mhz=$(( $(< /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) / 1000 )) + else + cpu_mhz=$(lscpu | awk -F "[ .]+" '/MHz/ {print $4;exit}') + fi + + # Determine correct string format for CPU clock speed + if [[ -n "$cpu_mhz" ]]; then + [[ "$cpu_mhz" -le "999" ]] && cpu_freq="$cpu_mhz MHz" || cpu_freq="$(calcFunc "$cpu_mhz"/1000) Ghz" + [[ -n "$cpu_freq" ]] && cpu_freq_str=" @ $cpu_freq" || cpu_freq_str="" + fi + + # Determine colour for temperature + if [[ -n "$temp_file" ]]; then + if [[ "$temp_unit" == "C" ]]; then + cpu_temp=$(printf "%'.0fc\n" "$(calcFunc "$(< $temp_file) / 1000")") + + case "${cpu_temp::-1}" in + -*|[0-9]|[1-3][0-9]) cpu_col="$COL_LIGHT_BLUE";; + 4[0-9]) cpu_col="";; + 5[0-9]) cpu_col="$COL_YELLOW";; + 6[0-9]) cpu_col="$COL_LIGHT_RED";; + *) cpu_col="$COL_URG_RED";; + esac + + # $COL_NC$COL_DARK_GRAY is needed for $COL_URG_RED + cpu_temp_str=", $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" + + elif [[ "$temp_unit" == "F" ]]; then + cpu_temp=$(printf "%'.0ff\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")") + + case "${cpu_temp::-1}" in + -*|[0-9]|[0-9][0-9]) cpu_col="$COL_LIGHT_BLUE";; + 1[0-1][0-9]) cpu_col="";; + 1[2-3][0-9]) cpu_col="$COL_YELLOW";; + 1[4-5][0-9]) cpu_col="$COL_LIGHT_RED";; + *) cpu_col="$COL_URG_RED";; + esac + + cpu_temp_str=", $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" + + else + cpu_temp_str=$(printf ", %'.0fk\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")") + fi + else + cpu_temp_str="" + fi + + ram_raw=($(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.0f %.0f %.0f", (total-free-buffers-cached)*100/total, (total-free-buffers-cached)*1024, total*1024}' /proc/meminfo)) + ram_perc="${ram_raw[0]}" + ram_used="${ram_raw[1]}" + ram_total="${ram_raw[2]}" + + if [[ "$(pihole status web 2> /dev/null)" == "1" ]]; then + ph_status="${COL_LIGHT_GREEN}Active" + else + ph_status="${COL_LIGHT_RED}Inactive" + fi + + if [[ "$DHCP_ACTIVE" == "true" ]]; then + ph_dhcp_num=$(wc -l 2> /dev/null < "/etc/pihole/dhcp.leases") + fi } -displayHelp() { - cat << EOM -::: Displays stats about your piHole! -::: -::: Usage: sudo pihole -c [optional:-j] -::: Note: If no option is passed, then stats are displayed on screen, updated every 5 seconds -::: -::: Options: -::: -j, --json output stats as JSON formatted string -::: -h, --help display this help text -EOM - exit 0 +get_ftl_stats() { + local stats_raw + + stats_raw=($(pihole-FTL "stats")) + domains_being_blocked_raw="${stats_raw[1]}" + dns_queries_today_raw="${stats_raw[3]}" + ads_blocked_today_raw="${stats_raw[5]}" + ads_percentage_today_raw="${stats_raw[7]}" + + # Only retrieve these stats when not called from jsonFunc + if [[ -z "$1" ]]; then + local recent_blocked_raw + local top_ad_raw + local top_domain_raw + local top_client_raw + + domains_being_blocked=$(printf "%'.0f\n" "${domains_being_blocked_raw}") + dns_queries_today=$(printf "%'.0f\n" "${dns_queries_today_raw}") + ads_blocked_today=$(printf "%'.0f\n" "${ads_blocked_today_raw}") + ads_percentage_today=$(printf "%'.0f\n" "${ads_percentage_today_raw}") + + recent_blocked_raw=$(pihole-FTL recentBlocked) + top_ad_raw=($(pihole-FTL "top-ads (1)")) + top_domain_raw=($(pihole-FTL "top-domains (1)")) + top_client_raw=($(pihole-FTL "top-clients (1)")) + + # Limit strings to 40 characters to prevent overflow + recent_blocked="${recent_blocked_raw:0:40}" + top_ad="${top_ad_raw[2]:0:40}" + top_domain="${top_domain_raw[2]:0:40}" + [[ "${top_client_raw[3]}" ]] && top_client="${top_client_raw[3]:0:40}" || top_client="${top_client_raw[2]:0:40}" + fi +} + +chronoFunc() { + get_init_stats + + for (( ; ; )); do + get_sys_stats + get_ftl_stats + + # Do not print LTE/FTL strings if API is unavailable + ph_core_str=" ${COL_DARK_GRAY}Pi-hole: $ph_core_ver${COL_NC}" + if [[ -n "$ph_lte_ver" ]]; then + ph_lte_str=" ${COL_DARK_GRAY}AdminLTE: $ph_lte_ver${COL_NC}" + ph_ftl_str=" ${COL_DARK_GRAY}FTL: $ph_ftl_ver${COL_NC}" + fi + + clear + + echo -e "|Β―Β―Β―(Β―)__|Β―|_ ___|Β―|___$ph_core_str +| Β―_/Β―|__| ' \/ _ \ / -_)$ph_lte_str +|_| |_| |_||_\___/_\___|$ph_ftl_str + ${COL_DARK_GRAY}β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”${COL_NC}" + + printFunc " Hostname: " "$sys_name" + [ -n "$sys_type" ] && printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$sys_type" "$COL_NC" || printf "\n" + + printf "%s\n" " Uptime: $sys_uptime" + + printFunc " Task Load: " "$sys_loadavg" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Active: $cpu_taskact of $cpu_tasks tasks" "$COL_NC" + + printFunc " CPU usage: " "$cpu_perc%" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$sys_cores $sys_cores_plu$cpu_freq_str$cpu_temp_str" "$COL_NC" + + printFunc " RAM usage: " "$ram_perc%" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Used: $(hrBytes "$ram_used") of $(hrBytes "$ram_total")" "$COL_NC" + + printFunc " HDD usage: " "$disk_perc" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Used: $(hrBytes "$disk_used") of $(hrBytes "$disk_total")" "$COL_NC" + + printFunc " LAN addr: " "${IPV4_ADDRESS/\/*/}" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Gateway: $net_gateway" "$COL_NC" + + if [[ "$DHCP_ACTIVE" == "true" ]]; then + printFunc " DHCP: " "$DHCP_START to $ph_dhcp_eip" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Leased: $ph_dhcp_num of $ph_dhcp_max" "$COL_NC" + fi + + printFunc " Pi-hole: " "$ph_status" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Blocking: $domains_being_blocked sites" "$COL_NC" + + printFunc " Ads Today: " "$ads_percentage_today%" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "$ads_blocked_today of $dns_queries_today queries" "$COL_NC" + + printFunc " Fwd DNS: " "$PIHOLE_DNS_1" + printf "%s(%s)%s\n" "$COL_DARK_GRAY" "Alt DNS: $ph_alts" "$COL_NC" + + echo -e " ${COL_DARK_GRAY}β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”β€”${COL_NC}" + echo " Recently blocked: $recent_blocked" + echo " Top Advertiser: $top_ad" + echo " Top Domain: $top_domain" + printFunc " Top Client: " "$top_client" "last" + + if [[ "$1" == "exit" ]]; then + exit 0 + else + if [[ -n "$1" ]]; then + sleep "${1}" + else + sleep 5 + fi + fi + + done +} + +jsonFunc() { + get_ftl_stats "json" + echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw}}" +} + +helpFunc() { + if [[ "$1" == "?" ]]; then + echo "Unknown option. Please view 'pihole -c --help' for more information" + else + echo "Usage: pihole -c [options] +Example: 'pihole -c -j' +Calculates stats and displays to an LCD + +Options: + -j, --json Output stats as JSON formatted string + -r, --refresh Set update frequency (in seconds) + -e, --exit Output stats and exit witout refreshing + -h, --help Display this help text" + fi + + exit 0 } if [[ $# = 0 ]]; then - normalChrono + chronoFunc fi for var in "$@"; do - case "$var" in - "-j" | "--json" ) outputJSON;; - "-h" | "--help" ) displayHelp;; - * ) exit 1;; - esac + case "$var" in + "-j" | "--json" ) jsonFunc;; + "-h" | "--help" ) helpFunc;; + "-r" | "--refresh" ) chronoFunc "$2";; + "-e" | "--exit" ) chronoFunc "exit";; + * ) helpFunc "?";; + esac done diff --git a/advanced/Scripts/list.sh b/advanced/Scripts/list.sh index 537ebac3..308e1f5e 100755 --- a/advanced/Scripts/list.sh +++ b/advanced/Scripts/list.sh @@ -3,14 +3,12 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# Whitelists and blacklists domains +# Whitelist and blacklist domains # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. - - -#globals +# Globals basename=pihole piholeDir=/etc/${basename} whitelist=${piholeDir}/whitelist.txt @@ -27,122 +25,118 @@ listMain="" listAlt="" helpFunc() { + if [[ "${listMain}" == "${whitelist}" ]]; then + param="w" + type="white" + elif [[ "${listMain}" == "${wildcardlist}" ]]; then + param="wild" + type="wildcard black" + else + param="b" + type="black" + fi - if [[ ${listMain} == ${whitelist} ]]; then - letter="w" - word="white" - else - letter="b" - word="black" - fi + echo "Usage: pihole -${param} [options] +Example: 'pihole -${param} site.com', or 'pihole -${param} site1.com site2.com' +${type^}list one or more domains - cat << EOM -::: Immediately ${word}lists one or more domains in the hosts file -::: -::: Usage: pihole -${letter} domain1 [domain2 ...] -::: -::: Options: -::: -d, --delmode Remove domains from the ${word}list -::: -nr, --noreload Update ${word}list without refreshing dnsmasq -::: -q, --quiet Output is less verbose -::: -h, --help Show this help dialog -::: -l, --list Display your ${word}listed domains -EOM -if [[ "${letter}" == "b" ]]; then - echo "::: -wild, --wildcard Add wildcard entry (only blacklist)" -fi - exit 0 +Options: + -d, --delmode Remove domain(s) from the ${type}list + -nr, --noreload Update ${type}list without refreshing dnsmasq + -q, --quiet Make output less verbose + -h, --help Show this help dialog + -l, --list Display all your ${type}listed domains" + + exit 0 } EscapeRegexp() { - # This way we may safely insert an arbitrary - # string in our regular expressions - # Also remove leading "." if present - echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g" + # This way we may safely insert an arbitrary + # string in our regular expressions + # Also remove leading "." if present + echo $* | sed 's/^\.*//' | sed "s/[]\.|$(){}?+*^]/\\\\&/g" | sed "s/\\//\\\\\//g" } -HandleOther(){ - # First, convert everything to lowercase - domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1") +HandleOther() { + # First, convert everything to lowercase + domain=$(sed -e "y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/" <<< "$1") - #check validity of domain - validDomain=$(echo "${domain}" | perl -lne 'print if /(?!.*[^a-z0-9-\.].*)^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+\.)*[a-z]{2,63}/') - if [ -z "${validDomain}" ]; then - echo "::: $1 is not a valid argument or domain name" - else - domList=("${domList[@]}" ${validDomain}) - fi + # Check validity of domain + validDomain=$(echo "${domain}" | perl -lne 'print if /(?!.*[^a-z0-9-\.].*)^((?=[a-z0-9-]{1,63}\.)(xn--)?[a-z0-9-]+\.)*[a-z]{2,63}/') + if [[ -z "${validDomain}" ]]; then + echo "::: $1 is not a valid argument or domain name" + else + domList=("${domList[@]}" ${validDomain}) + fi } PoplistFile() { - #check whitelist file exists, and if not, create it - if [[ ! -f ${whitelist} ]]; then - touch ${whitelist} - fi - for dom in "${domList[@]}"; do - # Logic : If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other - if ${addmode}; then - AddDomain "${dom}" "${listMain}" - RemoveDomain "${dom}" "${listAlt}" - if [[ "${listMain}" == "${whitelist}" || "${listMain}" == "${blacklist}" ]]; then - RemoveDomain "${dom}" "${wildcardlist}" - fi - else - RemoveDomain "${dom}" "${listMain}" - fi - done + # Check whitelist file exists, and if not, create it + if [[ ! -f ${whitelist} ]]; then + touch ${whitelist} + fi + + for dom in "${domList[@]}"; do + # Logic: If addmode then add to desired list and remove from the other; if delmode then remove from desired list but do not add to the other + if ${addmode}; then + AddDomain "${dom}" "${listMain}" + RemoveDomain "${dom}" "${listAlt}" + if [[ "${listMain}" == "${whitelist}" || "${listMain}" == "${blacklist}" ]]; then + RemoveDomain "${dom}" "${wildcardlist}" + fi + else + RemoveDomain "${dom}" "${listMain}" + fi + done } AddDomain() { - list="$2" - domain=$(EscapeRegexp "$1") + list="$2" + domain=$(EscapeRegexp "$1") - if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then + if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then + bool=true + # Is the domain in the list we want to add it to? + grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false - bool=true - #Is the domain in the list we want to add it to? - grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false + if [[ "${bool}" == false ]]; then + # Domain not found in the whitelist file, add it! + if [[ "${verbose}" == true ]]; then + echo "::: Adding $1 to $list..." + fi + reload=true + # Add it to the list we want to add it to + echo "$1" >> "${list}" + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} already exists in ${list}, no need to add!" + fi + fi + elif [[ "${list}" == "${wildcardlist}" ]]; then + source "${piholeDir}/setupVars.conf" + # Remove the /* from the end of the IPv4addr. + IPV4_ADDRESS=${IPV4_ADDRESS%/*} + IPV6_ADDRESS=${IPV6_ADDRESS} - if [[ "${bool}" == false ]]; then - #domain not found in the whitelist file, add it! - if [[ "${verbose}" == true ]]; then - echo "::: Adding $1 to $list..." - fi - reload=true - # Add it to the list we want to add it to - echo "$1" >> "${list}" - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} already exists in ${list}, no need to add!" - fi - fi + bool=true + # Is the domain in the list? + grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false - elif [[ "${list}" == "${wildcardlist}" ]]; then - - source "${piholeDir}/setupVars.conf" - #Remove the /* from the end of the IPv4addr. - IPV4_ADDRESS=${IPV4_ADDRESS%/*} - IPV6_ADDRESS=${IPV6_ADDRESS} - - bool=true - #Is the domain in the list? - grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false - - if [[ "${bool}" == false ]]; then - if [[ "${verbose}" == true ]]; then - echo "::: Adding $1 to wildcard blacklist..." - fi - reload=true - echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}" - if [[ ${#IPV6_ADDRESS} > 0 ]] ; then - echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}" - fi - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} already exists in wildcard blacklist, no need to add!" - fi - fi - fi + if [[ "${bool}" == false ]]; then + if [[ "${verbose}" == true ]]; then + echo "::: Adding $1 to wildcard blacklist..." + fi + reload=true + echo "address=/$1/${IPV4_ADDRESS}" >> "${wildcardlist}" + if [[ "${#IPV6_ADDRESS}" > 0 ]]; then + echo "address=/$1/${IPV6_ADDRESS}" >> "${wildcardlist}" + fi + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} already exists in wildcard blacklist, no need to add!" + fi + fi + fi } RemoveDomain() { @@ -150,85 +144,82 @@ RemoveDomain() { domain=$(EscapeRegexp "$1") if [[ "${list}" == "${whitelist}" || "${list}" == "${blacklist}" ]]; then - - bool=true - #Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa - grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false - if [[ "${bool}" == true ]]; then - # Remove it from the other one - echo "::: Removing $1 from $list..." - # /I flag: search case-insensitive - sed -i "/${domain}/Id" "${list}" - reload=true - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} does not exist in ${list}, no need to remove!" - fi + bool=true + # Is it in the list? Logic follows that if its whitelisted it should not be blacklisted and vice versa + grep -Ex -q "${domain}" "${list}" > /dev/null 2>&1 || bool=false + if [[ "${bool}" == true ]]; then + # Remove it from the other one + echo "::: Removing $1 from $list..." + # /I flag: search case-insensitive + sed -i "/${domain}/Id" "${list}" + reload=true + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} does not exist in ${list}, no need to remove!" fi - + fi elif [[ "${list}" == "${wildcardlist}" ]]; then - - bool=true - #Is it in the list? - grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false - if [[ "${bool}" == true ]]; then - # Remove it from the other one - echo "::: Removing $1 from $list..." - # /I flag: search case-insensitive - sed -i "/address=\/${domain}/Id" "${list}" - reload=true - else - if [[ "${verbose}" == true ]]; then - echo "::: ${1} does not exist in ${list}, no need to remove!" - fi + bool=true + # Is it in the list? + grep -e "address=\/${domain}\/" "${wildcardlist}" > /dev/null 2>&1 || bool=false + if [[ "${bool}" == true ]]; then + # Remove it from the other one + echo "::: Removing $1 from $list..." + # /I flag: search case-insensitive + sed -i "/address=\/${domain}/Id" "${list}" + reload=true + else + if [[ "${verbose}" == true ]]; then + echo "::: ${1} does not exist in ${list}, no need to remove!" fi + fi fi } Reload() { - # Reload hosts file - pihole -g -sd + # Reload hosts file + pihole -g -sd } Displaylist() { - if [[ ${listMain} == ${whitelist} ]]; then - string="gravity resistant domains" - else - string="domains caught in the sinkhole" - fi - verbose=false - echo -e " Displaying $string \n" - count=1 - while IFS= read -r RD; do - echo "${count}: ${RD}" - count=$((count+1)) - done < "${listMain}" - exit 0; + if [[ "${listMain}" == "${whitelist}" ]]; then + string="gravity resistant domains" + else + string="domains caught in the sinkhole" + fi + verbose=false + echo -e "Displaying $string:\n" + count=1 + while IFS= read -r RD; do + echo "${count}: ${RD}" + count=$((count+1)) + done < "${listMain}" + exit 0; } for var in "$@"; do - case "${var}" in - "-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";; - "-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";; - "-wild" | "wildcard" ) listMain="${wildcardlist}";; - "-nr"| "--noreload" ) reload=false;; - "-d" | "--delmode" ) addmode=false;; - "-f" | "--force" ) force=true;; - "-q" | "--quiet" ) verbose=false;; - "-h" | "--help" ) helpFunc;; - "-l" | "--list" ) Displaylist;; - * ) HandleOther "${var}";; - esac + case "${var}" in + "-w" | "whitelist" ) listMain="${whitelist}"; listAlt="${blacklist}";; + "-b" | "blacklist" ) listMain="${blacklist}"; listAlt="${whitelist}";; + "-wild" | "wildcard" ) listMain="${wildcardlist}";; + "-nr"| "--noreload" ) reload=false;; + "-d" | "--delmode" ) addmode=false;; + "-f" | "--force" ) force=true;; + "-q" | "--quiet" ) verbose=false;; + "-h" | "--help" ) helpFunc;; + "-l" | "--list" ) Displaylist;; + * ) HandleOther "${var}";; + esac done shift if [[ $# = 0 ]]; then - helpFunc + helpFunc fi PoplistFile if ${reload}; then - Reload + Reload fi diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 09f20d6b..e2c0ab11 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -3,7 +3,7 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# Checkout other branches than master +# Switch Pi-hole subsystems to a different Github branch # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. @@ -18,9 +18,12 @@ PH_TEST="true" source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" # setupVars set in basic-install.sh source "${setupVars}" - update="false" +# Colour codes +red="\e[1;31m" +def="\e[0m" + fully_fetch_repo() { # Add upstream branches to shallow clone local directory="${1}" @@ -35,7 +38,7 @@ fully_fetch_repo() { return 0 } -get_available_branches(){ +get_available_branches() { # Return available branches local directory="${1}" @@ -54,6 +57,8 @@ fetch_checkout_pull_branch() { # Set the reference for the requested branch, fetch, check it put and pull it cd "${directory}" git remote set-branches origin "${branch}" || return 1 + git stash --all --quiet &> /dev/null || true + git clean --force -d || true git fetch --quiet || return 1 checkout_pull_branch "${directory}" "${branch}" || return 1 } @@ -79,23 +84,23 @@ checkout_pull_branch() { } warning1() { - echo "::: Note that changing the branch is a severe change of your Pi-hole system." - echo "::: This is not supported unless one of the developers explicitly asks you to do this!" - read -r -p "::: Have you read and understood this? [y/N] " response + 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 -e " ${red}This feature is NOT supported unless a Pi-hole developer explicitly asks!${def}" + read -r -p " Have you read and understood this? [y/N] " response case ${response} in [yY][eE][sS]|[yY]) - echo "::: Continuing." + echo "::: Continuing with branch change." return 0 ;; *) - echo "::: Aborting." + echo "::: Branch change has been cancelled." return 1 ;; esac } -checkout() -{ +checkout() { local corebranches local webbranches @@ -192,11 +197,10 @@ checkout() if [[ ! "${1}" == "web" && "${update}" == "true" ]]; then echo "::: Running installer to upgrade your installation" if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then - exit 0 + exit 0 else - echo "Unable to complete update, contact Pi-hole" - exit 1 + echo "Unable to complete update, contact Pi-hole" + exit 1 fi fi } - diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index 10dd1e8b..8020cc80 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -260,18 +260,18 @@ ip_ping_check() { if [[ -n ${ip_def_gateway} ]]; then echo -n "::: Pinging default IPv${protocol} gateway: " if ! ping_gateway="$(${cmd} -q -W 3 -c 3 -n ${ip_def_gateway} -I ${PIHOLE_INTERFACE} | tail -n 3)"; then - echo "Gateway did not respond." + log_echo "Gateway did not respond." return 1 else - echo "Gateway responded." + log_echo "Gateway responded." log_write "${ping_gateway}" fi echo -n "::: Pinging Internet via IPv${protocol}: " if ! ping_inet="$(${cmd} -q -W 3 -c 3 -n ${g_addr} -I ${PIHOLE_INTERFACE} | tail -n 3)"; then - echo "Query did not respond." + log_echo "Query did not respond." return 1 else - echo "Query responded." + log_echo "Query responded." log_write "${ping_inet}" fi else diff --git a/advanced/Scripts/piholeLogFlush.sh b/advanced/Scripts/piholeLogFlush.sh index fd66b255..cc553b32 100755 --- a/advanced/Scripts/piholeLogFlush.sh +++ b/advanced/Scripts/piholeLogFlush.sh @@ -8,17 +8,38 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. -echo -n "::: Flushing /var/log/pihole.log ..." -# Test if logrotate is available on this system -if command -v /usr/sbin/logrotate &> /dev/null; then - # Flush twice to move all data out of sight of FTL - /usr/sbin/logrotate --force /etc/pihole/logrotate - /usr/sbin/logrotate --force /etc/pihole/logrotate +if [[ "$@" != *"quiet"* ]]; then + echo -n "::: Flushing /var/log/pihole.log ..." +fi +if [[ "$@" == *"once"* ]]; then + # Nightly logrotation + if command -v /usr/sbin/logrotate >/dev/null; then + # Logrotate once + /usr/sbin/logrotate --force /etc/pihole/logrotate + else + # Copy pihole.log over to pihole.log.1 + # and empty out pihole.log + # Note that moving the file is not an option, as + # dnsmasq would happily continue writing into the + # moved file (it will have the same file handler) + cp /var/log/pihole.log /var/log/pihole.log.1 + echo " " > /var/log/pihole.log + fi else - # Flush both pihole.log and pihole.log.1 (if existing) - echo " " > /var/log/pihole.log - if [ -f /var/log/pihole.log.1 ]; then - echo " " > /var/log/pihole.log.1 + # Manual flushing + if command -v /usr/sbin/logrotate >/dev/null; then + # Logrotate twice to move all data out of sight of FTL + /usr/sbin/logrotate --force /etc/pihole/logrotate; sleep 3 + /usr/sbin/logrotate --force /etc/pihole/logrotate + else + # Flush both pihole.log and pihole.log.1 (if existing) + echo " " > /var/log/pihole.log + if [ -f /var/log/pihole.log.1 ]; then + echo " " > /var/log/pihole.log.1 + fi fi fi -echo "... done!" + +if [[ "$@" != *"quiet"* ]]; then + echo "... done!" +fi diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 4fceb931..6aef183b 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -117,7 +117,10 @@ main() { echo "::: FTL: up to date" fi - if ${FTL_update}; then + # Logic: Don't update FTL when there is a core update available + # since the core update will run the installer which will itself + # re-install (i.e. update) FTL + if ${FTL_update} && ! ${core_update}; then echo ":::" echo "::: FTL out of date" FTLdetect @@ -194,21 +197,21 @@ main() { if [[ "${web_update}" == true ]]; then web_version_current="$(/usr/local/bin/pihole version --admin --current)" echo ":::" - echo "::: Web Admin version is now at ${web_version_current}" + echo "::: Web Admin version is now at ${web_version_current/* v/v}}" echo "::: If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'" fi if [[ "${core_update}" == true ]]; then pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)" echo ":::" - echo "::: Pi-hole version is now at ${pihole_version_current}" + echo "::: Pi-hole version is now at ${pihole_version_current/* v/v}}" echo "::: If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'" fi if [[ ${FTL_update} == true ]]; then - FTL_version_current="$(/usr/bin/pihole-FTL tag)" + FTL_version_current="$(/usr/local/bin/pihole version --ftl --current)" echo ":::" - echo "::: FTL version is now at ${FTL_version_current}" + echo "::: FTL version is now at ${FTL_version_current/* v/v}}" start_service pihole-FTL enable_service pihole-FTL fi diff --git a/advanced/Scripts/version.sh b/advanced/Scripts/version.sh index 7f96e29a..f5e0f51d 100755 --- a/advanced/Scripts/version.sh +++ b/advanced/Scripts/version.sh @@ -3,24 +3,29 @@ # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# shows version numbers +# Show version numbers # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. # Variables DEFAULT="-1" -PHGITDIR="/etc/.pihole/" +COREGITDIR="/etc/.pihole/" WEBGITDIR="/var/www/html/admin/" getLocalVersion() { + # FTL requires a different method + if [[ "$1" == "FTL" ]]; then + pihole-FTL version + return 0 + fi + # Get the tagged version of the local repository local directory="${1}" local version - cd "${directory}" || { echo "${DEFAULT}"; return 1; } - version=$(git describe --tags --always || \ - echo "${DEFAULT}") + cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; } + version=$(git describe --tags --always || echo "$DEFAULT") if [[ "${version}" =~ ^v ]]; then echo "${version}" elif [[ "${version}" == "${DEFAULT}" ]]; then @@ -33,13 +38,18 @@ getLocalVersion() { } getLocalHash() { + # Local FTL hash does not exist on filesystem + if [[ "$1" == "FTL" ]]; then + echo "N/A" + return 0 + fi + # Get the short hash of the local repository local directory="${1}" local hash - cd "${directory}" || { echo "${DEFAULT}"; return 1; } - hash=$(git rev-parse --short HEAD || \ - echo "${DEFAULT}") + cd "${directory}" 2> /dev/null || { echo "${DEFAULT}"; return 1; } + hash=$(git rev-parse --short HEAD || echo "$DEFAULT") if [[ "${hash}" == "${DEFAULT}" ]]; then echo "ERROR" return 1 @@ -49,12 +59,33 @@ getLocalHash() { return 0 } +getRemoteHash(){ + # Remote FTL hash is not applicable + if [[ "$1" == "FTL" ]]; then + echo "N/A" + return 0 + fi + + local daemon="${1}" + local branch="${2}" + + hash=$(git ls-remote --heads "https://github.com/pi-hole/${daemon}" | \ + awk -v bra="$branch" '$0~bra {print substr($0,0,8);exit}') + if [[ -n "$hash" ]]; then + echo "$hash" + else + echo "ERROR" + return 1 + fi + return 0 +} + getRemoteVersion(){ # Get the version from the remote origin local daemon="${1}" local version - version=$(curl --silent --fail https://api.github.com/repos/pi-hole/${daemon}/releases/latest | \ + version=$(curl --silent --fail "https://api.github.com/repos/pi-hole/${daemon}/releases/latest" | \ awk -F: '$1 ~/tag_name/ { print $2 }' | \ tr -cd '[[:alnum:]]._-') if [[ "${version}" =~ ^v ]]; then @@ -66,72 +97,72 @@ getRemoteVersion(){ return 0 } -#PHHASHLATEST=$(curl -s https://api.github.com/repos/pi-hole/pi-hole/commits/master | \ -# grep sha | \ -# head -n1 | \ -# awk -F ' ' '{ print $2 }' | \ -# tr -cd '[[:alnum:]]._-') - -#WEBHASHLATEST=$(curl -s https://api.github.com/repos/pi-hole/AdminLTE/commits/master | \ -# grep sha | \ -# head -n1 | \ -# awk -F ' ' '{ print $2 }' | \ -# tr -cd '[[:alnum:]]._-') - - -normalOutput() { - echo "::: Pi-hole version is $(getLocalVersion "${PHGITDIR}") (Latest version is $(getRemoteVersion pi-hole))" - if [ -d "${WEBGITDIR}" ]; then - echo "::: Web-Admin version is $(getLocalVersion "${WEBGITDIR}") (Latest version is $(getRemoteVersion AdminLTE))" - fi -} - -webOutput() { - if [ -d "${WEBGITDIR}" ]; then - case "${1}" in - "-l" | "--latest" ) echo $(getRemoteVersion AdminLTE);; - "-c" | "--current" ) echo $(getLocalVersion "${WEBGITDIR}");; - "-h" | "--hash" ) echo $(getLocalHash "${WEBGITDIR}");; - * ) echo "::: Invalid Option!"; exit 1; - esac - else - echo "::: Web interface not installed!"; exit 1; +versionOutput() { + [[ "$1" == "pi-hole" ]] && GITDIR=$COREGITDIR + [[ "$1" == "AdminLTE" ]] && GITDIR=$WEBGITDIR + [[ "$1" == "FTL" ]] && GITDIR="FTL" + + [[ "$2" == "-c" ]] || [[ "$2" == "--current" ]] || [[ -z "$2" ]] && current=$(getLocalVersion $GITDIR) + [[ "$2" == "-l" ]] || [[ "$2" == "--latest" ]] || [[ -z "$2" ]] && latest=$(getRemoteVersion "$1") + if [[ "$2" == "-h" ]] || [[ "$2" == "--hash" ]]; then + [[ "$3" == "-c" ]] || [[ "$3" == "--current" ]] || [[ -z "$3" ]] && curHash=$(getLocalHash "$GITDIR") + [[ "$3" == "-l" ]] || [[ "$3" == "--latest" ]] || [[ -z "$3" ]] && latHash=$(getRemoteHash "$1" "$(cd "$GITDIR" 2> /dev/null && git rev-parse --abbrev-ref HEAD)") fi + + if [[ -n "$current" ]] && [[ -n "$latest" ]]; then + output="${1^} version is $current (Latest: $latest)" + elif [[ -n "$current" ]] && [[ -z "$latest" ]]; then + output="Current ${1^} version is $current" + elif [[ -z "$current" ]] && [[ -n "$latest" ]]; then + output="Latest ${1^} version is $latest" + elif [[ "$curHash" == "N/A" ]] || [[ "$latHash" == "N/A" ]]; then + output="${1^} hash is not applicable" + elif [[ -n "$curHash" ]] && [[ -n "$latHash" ]]; then + output="${1^} hash is $curHash (Latest: $latHash)" + elif [[ -n "$curHash" ]] && [[ -z "$latHash" ]]; then + output="Current ${1^} hash is $curHash" + elif [[ -z "$curHash" ]] && [[ -n "$latHash" ]]; then + output="Latest ${1^} hash is $latHash" + else + errorOutput + fi + + [[ -n "$output" ]] && echo " $output" } -coreOutput() { - case "${1}" in - "-l" | "--latest" ) echo $(getRemoteVersion pi-hole);; - "-c" | "--current" ) echo $(getLocalVersion "${PHGITDIR}");; - "-h" | "--hash" ) echo $(getLocalHash "${PHGITDIR}");; - * ) echo "::: Invalid Option!"; exit 1; - esac +errorOutput() { + echo " Invalid Option! Try 'pihole -v --help' for more information." + exit 1 +} + +defaultOutput() { + versionOutput "pi-hole" "$@" + versionOutput "AdminLTE" "$@" + versionOutput "FTL" "$@" } helpFunc() { - cat << EOM -::: -::: Show Pi-hole/Web Admin versions -::: -::: Usage: pihole -v [ -a | -p ] [ -l | -c ] -::: -::: Options: -::: -a, --admin Show both current and latest versions of web admin -::: -p, --pihole Show both current and latest versions of Pi-hole core files -::: -l, --latest (Only after -a | -p) Return only latest version -::: -c, --current (Only after -a | -p) Return only current version -::: -h, --help Show this help dialog -::: -EOM - exit 0 + echo "Usage: pihole -v [repo | option] [option] +Example: 'pihole -v -p -l' +Show Pi-hole, Admin Console & FTL versions + +Repositories: + -p, --pihole Only retrieve info regarding Pi-hole repository + -a, --admin Only retrieve info regarding AdminLTE repository + -f, --ftl Only retrieve info regarding FTL repository + +Options: + -c, --current Return the current version + -l, --latest Return the latest version + --hash Return the Github hash from your local repositories + -h, --help Show this help dialog" + exit 0 } -if [[ $# = 0 ]]; then - normalOutput -fi - case "${1}" in - "-a" | "--admin" ) shift; webOutput "$@";; - "-p" | "--pihole" ) shift; coreOutput "$@" ;; + "-p" | "--pihole" ) shift; versionOutput "pi-hole" "$@";; + "-a" | "--admin" ) shift; versionOutput "AdminLTE" "$@";; + "-f" | "--ftl" ) shift; versionOutput "FTL" "$@";; "-h" | "--help" ) helpFunc;; + * ) defaultOutput "$@";; esac diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index d3ad3032..8419aa8d 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -8,7 +8,6 @@ # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. - readonly setupVars="/etc/pihole/setupVars.conf" readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf" readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" @@ -16,23 +15,19 @@ readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf" readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf" helpFunc() { - cat << EOM -::: Set admin options for the web interface of pihole -::: -::: Usage: pihole -a [options] -::: -::: Options: -::: -p, password Set web interface password, an empty input will remove any previously set password -::: -c, celsius Set Celsius temperature unit -::: -f, fahrenheit Set Fahrenheit temperature unit -::: -k, kelvin Set Kelvin temperature unit -::: -h, --help Show this help dialog -::: -i, interface Setup interface listening behavior of dnsmasq -::: pihole -a -i local : Listen on all interfaces, but allow only queries from -::: devices that are at most one hop away (local devices) -::: pihole -a -i single : Listen only on one interface (see PIHOLE_INTERFACE) -::: pihole -a -i all : Listen on all interfaces, permit all origins -EOM + echo "Usage: pihole -a [options] +Example: pihole -a -p password +Set options for the Admin Console + +Options: + -f, flush Flush the Pi-hole log + -p, password Set Admin Console password + -c, celsius Set Celsius as preferred temperature unit + -f, fahrenheit Set Fahrenheit as preferred temperature unit + -k, kelvin Set Kelvin as preferred temperature unit + -h, --help Show this help dialog + -i, interface Specify dnsmasq's interface listening behavior + Add '-h' for more info on interface usage" exit 0 } @@ -61,21 +56,18 @@ delete_dnsmasq_setting() { sed -i "/${1}/d" "${dnsmasqconfig}" } -SetTemperatureUnit(){ - +SetTemperatureUnit() { change_setting "TEMPERATUREUNIT" "${unit}" - } -HashPassword(){ - # Compute password hash twice to avoid rainbow table vulnerability - return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//') - return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//') - echo ${return} +HashPassword() { + # Compute password hash twice to avoid rainbow table vulnerability + return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//') + return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//') + echo ${return} } -SetWebPassword(){ - +SetWebPassword() { if [ "${SUDO_USER}" == "www-data" ]; then echo "Security measure: user www-data is not allowed to change webUI password!" echo "Exiting" @@ -175,8 +167,7 @@ trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE3 } -SetDNSServers(){ - +SetDNSServers() { # Save setting to file delete_setting "PIHOLE_DNS" IFS=',' read -r -a array <<< "${args[2]}" @@ -207,72 +198,59 @@ SetDNSServers(){ # Restart dnsmasq to load new configuration RestartDNS - } -SetExcludeDomains(){ - +SetExcludeDomains() { change_setting "API_EXCLUDE_DOMAINS" "${args[2]}" - } -SetExcludeClients(){ - +SetExcludeClients() { change_setting "API_EXCLUDE_CLIENTS" "${args[2]}" - } -Reboot(){ - +Reboot() { nohup bash -c "sleep 5; reboot" &> /dev/null /dev/null else service dnsmasq restart &> /dev/null fi - } -SetQueryLogOptions(){ - +SetQueryLogOptions() { change_setting "API_QUERY_LOG_SHOW" "${args[2]}" - } ProcessDHCPSettings() { - source "${setupVars}" if [[ "${DHCP_ACTIVE}" == "true" ]]; then + interface=$(grep 'PIHOLE_INTERFACE=' /etc/pihole/setupVars.conf | sed "s/.*=//") - interface=$(grep 'PIHOLE_INTERFACE=' /etc/pihole/setupVars.conf | sed "s/.*=//") + # Use eth0 as fallback interface + if [ -z ${interface} ]; then + interface="eth0" + fi - # Use eth0 as fallback interface - if [ -z ${interface} ]; then - interface="eth0" - fi + if [[ "${PIHOLE_DOMAIN}" == "" ]]; then + PIHOLE_DOMAIN="local" + change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" + fi - if [[ "${PIHOLE_DOMAIN}" == "" ]]; then - PIHOLE_DOMAIN="local" - change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}" - fi + if [[ "${DHCP_LEASETIME}" == "0" ]]; then + leasetime="infinite" + elif [[ "${DHCP_LEASETIME}" == "" ]]; then + leasetime="24h" + change_setting "DHCP_LEASETIME" "${leasetime}" + else + leasetime="${DHCP_LEASETIME}h" + fi - if [[ "${DHCP_LEASETIME}" == "0" ]]; then - leasetime="infinite" - elif [[ "${DHCP_LEASETIME}" == "" ]]; then - leasetime="24h" - change_setting "DHCP_LEASETIME" "${leasetime}" - else - leasetime="${DHCP_LEASETIME}h" - fi - - # Write settings to file - echo "############################################################################### + # Write settings to file + echo "############################################################################### # DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE. # # ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE # ############################################################################### @@ -283,26 +261,25 @@ dhcp-leasefile=/etc/pihole/dhcp.leases #quiet-dhcp " > "${dhcpconfig}" -if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then - echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" -fi + if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then + echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}" + fi - if [[ "${DHCP_IPv6}" == "true" ]]; then -echo "#quiet-dhcp6 + if [[ "${DHCP_IPv6}" == "true" ]]; then + echo "#quiet-dhcp6 #enable-ra dhcp-option=option6:dns-server,[::] dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime} ra-param=*,0,0 " >> "${dhcpconfig}" - fi + fi else rm "${dhcpconfig}" &> /dev/null fi } -EnableDHCP(){ - +EnableDHCP() { change_setting "DHCP_ACTIVE" "true" change_setting "DHCP_START" "${args[2]}" change_setting "DHCP_END" "${args[3]}" @@ -320,8 +297,7 @@ EnableDHCP(){ RestartDNS } -DisableDHCP(){ - +DisableDHCP() { change_setting "DHCP_ACTIVE" "false" # Remove possible old setting from file @@ -333,23 +309,20 @@ DisableDHCP(){ RestartDNS } -SetWebUILayout(){ - +SetWebUILayout() { change_setting "WEBUIBOXEDLAYOUT" "${args[2]}" - } CustomizeAdLists() { - list="/etc/pihole/adlists.list" - if [[ "${args[2]}" == "enable" ]] ; then + if [[ "${args[2]}" == "enable" ]]; then sed -i "\\@${args[3]}@s/^#http/http/g" "${list}" - elif [[ "${args[2]}" == "disable" ]] ; then + elif [[ "${args[2]}" == "disable" ]]; then sed -i "\\@${args[3]}@s/^http/#http/g" "${list}" - elif [[ "${args[2]}" == "add" ]] ; then + elif [[ "${args[2]}" == "add" ]]; then echo "${args[3]}" >> ${list} - elif [[ "${args[2]}" == "del" ]] ; then + elif [[ "${args[2]}" == "del" ]]; then var=$(echo "${args[3]}" | sed 's/\//\\\//g') sed -i "/${var}/Id" "${list}" else @@ -358,18 +331,15 @@ CustomizeAdLists() { fi } -SetPrivacyMode(){ - - if [[ "${args[2]}" == "true" ]] ; then +SetPrivacyMode() { + if [[ "${args[2]}" == "true" ]]; then change_setting "API_PRIVACY_MODE" "true" else change_setting "API_PRIVACY_MODE" "false" fi - } ResolutionSettings() { - typ="${args[2]}" state="${args[3]}" @@ -378,11 +348,9 @@ ResolutionSettings() { elif [[ "${typ}" == "clients" ]]; then change_setting "API_GET_CLIENT_HOSTNAME" "${state}" fi - } AddDHCPStaticAddress() { - mac="${args[2]}" ip="${args[3]}" host="${args[4]}" @@ -397,18 +365,14 @@ AddDHCPStaticAddress() { # Full info given echo "dhcp-host=${mac},${ip},${host}" >> "${dhcpstaticconfig}" fi - } RemoveDHCPStaticAddress() { - mac="${args[2]}" sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}" - } -SetHostRecord(){ - +SetHostRecord() { if [ -n "${args[3]}" ]; then change_setting "HOSTRECORD" "${args[2]},${args[3]}" echo "Setting host record for ${args[2]} -> ${args[3]}" @@ -421,17 +385,28 @@ SetHostRecord(){ # Restart dnsmasq to load new configuration RestartDNS - } -SetListeningMode(){ - +SetListeningMode() { source "${setupVars}" + + if [[ "$3" == "-h" ]] || [[ "$3" == "--help" ]]; then + echo "Usage: pihole -a -i [interface] +Example: 'pihole -a -i local' +Specify dnsmasq's network interface listening behavior - if [[ "${args[2]}" == "all" ]] ; then +Interfaces: + local Listen on all interfaces, but only allow queries from + devices that are at most one hop away (local devices) + single Listen only on ${PIHOLE_INTERFACE} interface + all Listen on all interfaces, permit all origins" + exit 0 + fi + + if [[ "${args[2]}" == "all" ]]; then echo "Listening on all interfaces, permiting all origins, hope you have a firewall!" change_setting "DNSMASQ_LISTENING" "all" - elif [[ "${args[2]}" == "local" ]] ; then + elif [[ "${args[2]}" == "local" ]]; then echo "Listening on all interfaces, permitting only origins that are at most one hop away (local devices)" change_setting "DNSMASQ_LISTENING" "local" else @@ -446,17 +421,14 @@ SetListeningMode(){ # Restart dnsmasq to load new configuration RestartDNS fi - } -Teleporter() -{ +Teleporter() { local datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S") php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip" } main() { - args=("$@") case "${args[1]}" in @@ -479,7 +451,7 @@ main() { "addstaticdhcp" ) AddDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;; "hostrecord" ) SetHostRecord;; - "-i" | "interface" ) SetListeningMode;; + "-i" | "interface" ) SetListeningMode "$@";; "-t" | "teleporter" ) Teleporter;; "adlist" ) CustomizeAdLists;; * ) helpFunc;; @@ -490,5 +462,4 @@ main() { if [[ $# = 0 ]]; then helpFunc fi - } diff --git a/advanced/index.php b/advanced/index.php index bfc44a1d..1dd5acc7 100644 --- a/advanced/index.php +++ b/advanced/index.php @@ -185,7 +185,7 @@ function add() { } $.ajax({ - url: "admin/scripts/pi-hole/php/add.php", + url: "/admin/scripts/pi-hole/php/add.php", method: "post", data: {"domain":domain.val(), "list":"white", "pw":pw.val()}, success: function(response) { diff --git a/advanced/logrotate b/advanced/logrotate index 570e7548..ffed910b 100644 --- a/advanced/logrotate +++ b/advanced/logrotate @@ -8,3 +8,14 @@ notifempty nomail } + +/var/log/pihole-FTL.log { + # su # + weekly + copytruncate + rotate 3 + compress + delaycompress + notifempty + nomail +} diff --git a/advanced/pihole-FTL.service b/advanced/pihole-FTL.service index 30cd140f..627fad8c 100644 --- a/advanced/pihole-FTL.service +++ b/advanced/pihole-FTL.service @@ -26,7 +26,7 @@ start() { echo "pihole-FTL is already running" else touch /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port - chown pihole:pihole /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port + chown pihole:pihole /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port /etc/pihole chmod 0644 /var/log/pihole-FTL.log /run/pihole-FTL.pid /run/pihole-FTL.port su -s /bin/sh -c "/usr/bin/pihole-FTL" "$FTLUSER" echo diff --git a/advanced/pihole.cron b/advanced/pihole.cron index c885b371..f1beb08c 100644 --- a/advanced/pihole.cron +++ b/advanced/pihole.cron @@ -21,9 +21,10 @@ # Pi-hole: Update Pi-hole! Uncomment to enable auto update #30 2 * * 7 root PATH="$PATH:/usr/local/bin/" pihole updatePihole -# Pi-hole: Flush the log daily at 00:00 so it doesn't get out of control -# Stats will be viewable in the Web interface thanks to the cron job above +# Pi-hole: Flush the log daily at 00:00 # The flush script will use logrotate if available -00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush +# parameter "once": logrotate only once (default is twice) +# parameter "quiet": don't print messages +00 00 * * * root PATH="$PATH:/usr/local/bin/" pihole flush once quiet @reboot root /usr/sbin/logrotate /etc/pihole/logrotate diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index c65ef49f..053e04ba 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -86,7 +86,7 @@ if command -v apt-get &> /dev/null; then #Debian Family ############################################# PKG_MANAGER="apt-get" - UPDATE_PKG_CACHE="test_dpkg_lock; ${PKG_MANAGER} update" + UPDATE_PKG_CACHE="${PKG_MANAGER} update" PKG_INSTALL=(${PKG_MANAGER} --yes --no-install-recommends install) # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true" @@ -220,6 +220,16 @@ getGitFiles() { return 0 } +resetRepo() { + local directory="${1}" + + cd "${directory}" &> /dev/null || return 1 + echo -n "::: Resetting repo in ${1}..." + git reset --hard &> /dev/null || return $? + echo " done!" + return 0 +} + find_IPv4_information() { local route # Find IP used to route to outside world @@ -317,16 +327,44 @@ chooseInterface() { fi } +# See https://github.com/pi-hole/pi-hole/issues/1473#issuecomment-301745953 +testIPv6() { + first="$(cut -f1 -d":" <<< "$1")" + value1=$(((0x$first)/256)) + value2=$(((0x$first)%256)) + ((($value1&254)==252)) && echo "ULA" || true + ((($value1&112)==32)) && echo "GUA" || true + ((($value1==254) && (($value2&192)==128))) && echo "Link-local" || true +} + useIPv6dialog() { - # Show the IPv6 address used for blocking - IPV6_ADDRESS=$(ip -6 route get 2001:4860:4860::8888 | grep -v "unreachable" | awk -F " " '{ for(i=1;i<=NF;i++) if ($i == "src") print $(i+1) }') + # Determine the IPv6 address used for blocking + IPV6_ADDRESSES=($(ip -6 address | grep 'scope global' | awk '{print $2}')) + + # Determine type of found IPv6 addresses + for i in "${IPV6_ADDRESSES[@]}"; do + result=$(testIPv6 "$i") + [[ "${result}" == "ULA" ]] && ULA_ADDRESS="$i" + [[ "${result}" == "GUA" ]] && GUA_ADDRESS="$i" + done + + # Determine which address to be used: Prefer ULA over GUA or don't use any if none found + if [[ ! -z "${ULA_ADDRESS}" ]]; then + IPV6_ADDRESS="${ULA_ADDRESS}" + echo "::: Found IPv6 ULA address, using it for blocking IPv6 ads" + elif [[ ! -z "${GUA_ADDRESS}" ]]; then + echo "::: Found IPv6 GUA address, using it for blocking IPv6 ads" + IPV6_ADDRESS="${GUA_ADDRESS}" + else + echo "::: Found neither IPv6 ULA nor GUA address, blocking IPv6 ads will not be enabled" + IPV6_ADDRESS="" + fi if [[ ! -z "${IPV6_ADDRESS}" ]]; then whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c} fi } - use4andor6() { local useIPv4 local useIPv6 @@ -408,7 +446,7 @@ setDHCPCD() { echo "interface ${PIHOLE_INTERFACE} static ip_address=${IPV4_ADDRESS} static routers=${IPv4gw} - static domain_name_servers=${IPv4gw}" | tee -a /etc/dhcpcd.conf >/dev/null + static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null } setStaticIPv4() { @@ -980,6 +1018,7 @@ configureFirewall() { iptables -C INPUT -p tcp -m tcp --dport 80 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT iptables -C INPUT -p tcp -m tcp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT iptables -C INPUT -p udp -m udp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p udp -m udp --dport 53 -j ACCEPT + iptables -C INPUT -p tcp -m tcp --dport 4711:4720 -i lo -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 4711:4720 -i lo -j ACCEPT return 0 fi else @@ -1041,7 +1080,7 @@ installLogrotate() { # the local properties of the /var/log directory logusergroup="$(stat -c '%U %G' /var/log)" if [[ ! -z $logusergroup ]]; then - sed -i "s/# su #/su ${logusergroup}/" /etc/pihole/logrotate + sed -i "s/# su #/su ${logusergroup}/g;" /etc/pihole/logrotate fi echo " done!" } @@ -1128,10 +1167,18 @@ checkSelinux() { displayFinalMessage() { + if [[ ${#1} -gt 0 ]] ; then + pwstring="$1" + elif [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) -gt 0 ]]; then + pwstring="unchanged" + else + pwstring="NOT SET" + fi + if [[ ${INSTALL_WEB} == true ]]; then additional="View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin -Your Admin Webpage login password is ${1:-"NOT SET"}" +Your Admin Webpage login password is ${pwstring}" fi # Final completion message to user @@ -1179,22 +1226,32 @@ update_dialogs() { } clone_or_update_repos() { -if [[ "${reconfigure}" == true ]]; then - echo "::: --reconfigure passed to install script. Not downloading/updating local repos" - else - # Get Git files for Core and Admin - getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || \ - { echo "!!! Unable to clone ${piholeGitUrl} into ${PI_HOLE_LOCAL_REPO}, unable to continue."; \ + if [[ "${reconfigure}" == true ]]; then + echo "::: --reconfigure passed to install script. Resetting changes to local repos" + resetRepo ${PI_HOLE_LOCAL_REPO} || \ + { echo "!!! Unable to reset ${PI_HOLE_LOCAL_REPO}, unable to continue."; \ + exit 1; \ + } + if [[ ${INSTALL_WEB} == true ]]; then + resetRepo ${webInterfaceDir} || \ + { echo "!!! Unable to reset ${webInterfaceDir}, unable to continue."; \ exit 1; \ } - - if [[ ${INSTALL_WEB} == true ]]; then - getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || \ - { echo "!!! Unable to clone ${webInterfaceGitUrl} into ${webInterfaceDir}, unable to continue."; \ - exit 1; \ - } - fi fi + else + # Get Git files for Core and Admin + getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || \ + { echo "!!! Unable to clone ${piholeGitUrl} into ${PI_HOLE_LOCAL_REPO}, unable to continue."; \ + exit 1; \ + } + + if [[ ${INSTALL_WEB} == true ]]; then + getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || \ + { echo "!!! Unable to clone ${webInterfaceGitUrl} into ${webInterfaceDir}, unable to continue."; \ + exit 1; \ + } + fi + fi } FTLinstall() { @@ -1220,6 +1277,7 @@ FTLinstall() { echo -n "transferred... " stop_service pihole-FTL &> /dev/null install -T -m 0755 /tmp/${binary} /usr/bin/pihole-FTL + rm /tmp/${binary} /tmp/${binary}.sha1 cd "${orig_dir}" install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/pihole-FTL.service" "/etc/init.d/pihole-FTL" echo "done." diff --git a/gravity.sh b/gravity.sh index a5231d5e..285ce5c3 100755 --- a/gravity.sh +++ b/gravity.sh @@ -54,6 +54,7 @@ IPV6_ADDRESS=${IPV6_ADDRESS} basename=pihole piholeDir=/etc/${basename} adList=${piholeDir}/gravity.list +blackList=${piholeDir}/black.list localList=${piholeDir}/local.list justDomainsExtension=domains matterAndLight=${basename}.0.matterandlight.txt @@ -235,9 +236,7 @@ gravity_Blacklist() { if [[ -f "${blacklistFile}" ]]; then numBlacklisted=$(wc -l < "${blacklistFile}") plural=; [[ "$numBlacklisted" != "1" ]] && plural=s - echo -n "::: Blacklisting $numBlacklisted domain${plural}..." - cat ${blacklistFile} >> ${piholeDir}/${eventHorizon} - echo " done!" + echo "::: Exact blocked domain${plural}: $numBlacklisted" else echo "::: Nothing to blacklist!" fi @@ -299,9 +298,25 @@ gravity_unique() { echo "::: $numberOf unique domains trapped in the event horizon." } -gravity_hostFormat() { +gravity_doHostFormat() { + # Check vars from setupVars.conf to see if we're using IPv4, IPv6, Or both. + if [[ -n "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then + # Both IPv4 and IPv6 + awk -v ipv4addr="$IPV4_ADDRESS" -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> "${2}" < "${1}" + elif [[ -n "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then + # Only IPv4 + awk -v ipv4addr="$IPV4_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0}' >> "${2}" < "${1}" + elif [[ -z "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then + # Only IPv6 + awk -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv6addr" "$0}' >> "${2}" < "${1}" + elif [[ -z "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then + echo "::: No IP Values found! Please run 'pihole -r' and choose reconfigure to restore values" + exit 1 + fi +} + +gravity_hostFormatLocal() { # Format domain list as "192.168.x.x domain.com" - echo -n "::: Formatting domains into a HOSTS file..." if [[ -f /etc/hostname ]]; then hostname=$( ${localList} - # Both IPv4 and IPv6 - cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPV4_ADDRESS" -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0"\n"ipv6addr" "$0}' >> ${piholeDir}/${accretionDisc} - - elif [[ -n "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then - - echo -e "${IPV4_ADDRESS} ${hostname}\n${IPV4_ADDRESS} pi.hole" > ${localList} - # Only IPv4 - cat ${piholeDir}/${eventHorizon} | awk -v ipv4addr="$IPV4_ADDRESS" '{sub(/\r$/,""); print ipv4addr" "$0}' >> ${piholeDir}/${accretionDisc} - - elif [[ -z "${IPV4_ADDRESS}" && -n "${IPV6_ADDRESS}" ]];then - - echo -e "${IPV6_ADDRESS} ${hostname}\n${IPV6_ADDRESS} pi.hole" > ${localList} - # Only IPv6 - cat ${piholeDir}/${eventHorizon} | awk -v ipv6addr="$IPV6_ADDRESS" '{sub(/\r$/,""); print ipv6addr" "$0}' >> ${piholeDir}/${accretionDisc} - - elif [[ -z "${IPV4_ADDRESS}" && -z "${IPV6_ADDRESS}" ]];then - echo "::: No IP Values found! Please run 'pihole -r' and choose reconfigure to restore values" - exit 1 - fi + echo -e "${hostname}\npi.hole" > "${localList}.tmp" + # Copy the file over as /etc/pihole/local.list so dnsmasq can use it + rm "${localList}" + gravity_doHostFormat "${localList}.tmp" "${localList}" + rm "${localList}.tmp" +} +gravity_hostFormatGravity() { + # Format domain list as "192.168.x.x domain.com" + echo "" > "${piholeDir}/${accretionDisc}" + gravity_doHostFormat "${piholeDir}/${eventHorizon}" "${piholeDir}/${accretionDisc}" # Copy the file over as /etc/pihole/gravity.list so dnsmasq can use it - cp ${piholeDir}/${accretionDisc} ${adList} - echo " done!" + mv "${piholeDir}/${accretionDisc}" "${adList}" +} + +gravity_hostFormatBlack() { + if [[ -f "${blacklistFile}" ]]; then + numBlacklisted=$(wc -l < "${blacklistFile}") + # Format domain list as "192.168.x.x domain.com" + gravity_doHostFormat "${blacklistFile}" "${blackList}.tmp" + # Copy the file over as /etc/pihole/black.list so dnsmasq can use it + mv "${blackList}.tmp" "${blackList}" + else + echo "::: Nothing to blacklist!" + fi } # blackbody - remove any remnant files from script processes @@ -377,11 +391,6 @@ gravity_advanced() { } gravity_reload() { - #Clear no longer needed files... - echo ":::" - echo -n "::: Cleaning up un-needed files..." - rm ${piholeDir}/pihole.*.txt - echo " done!" # Reload hosts file echo ":::" @@ -402,6 +411,7 @@ for var in "$@"; do "-f" | "--force" ) forceGrav=true;; "-h" | "--help" ) helpFunc;; "-sd" | "--skip-download" ) skipDownload=true;; + "-b" | "--blacklist-only" ) blackListOnly=true;; esac done @@ -411,22 +421,39 @@ if [[ "${forceGrav}" == true ]]; then echo " done!" fi -gravity_collapse -gravity_spinup -if [[ "${skipDownload}" == false ]]; then +if [[ ! "${blackListOnly}" == true ]]; then + gravity_collapse + gravity_spinup + if [[ "${skipDownload}" == false ]]; then gravity_Schwarzchild gravity_advanced -else + else echo "::: Using cached Event Horizon list..." numberOf=$(wc -l < ${piholeDir}/${preEventHorizon}) - echo "::: $numberOf unique domains trapped in the event horizon." + echo "::: $numberOf unique domains trapped in the event horizon." + fi + gravity_Whitelist fi -gravity_Whitelist gravity_Blacklist gravity_Wildcard -gravity_hostFormat +echo -n "::: Formatting domains into a HOSTS file..." +if [[ ! "${blackListOnly}" == true ]]; then + gravity_hostFormatLocal + gravity_hostFormatGravity +fi +gravity_hostFormatBlack +echo " done!" + gravity_blackbody +if [[ ! "${blackListOnly}" == true ]]; then + #Clear no longer needed files... + echo ":::" + echo -n "::: Cleaning up un-needed files..." + rm ${piholeDir}/pihole.*.txt + echo " done!" +fi + gravity_reload "${PIHOLE_COMMAND}" status diff --git a/pihole b/pihole index 83e13000..055d6bce 100755 --- a/pihole +++ b/pihole @@ -9,11 +9,11 @@ # Please see LICENSE file for your rights under this license. readonly PI_HOLE_SCRIPT_DIR="/opt/pihole" - readonly wildcardlist="/etc/dnsmasq.d/03-pihole-wildcard.conf" + # Must be root to use this tool if [[ ! $EUID -eq 0 ]];then - if [ -x "$(command -v sudo)" ];then + if [[ -x "$(command -v sudo)" ]]; then exec sudo bash "$0" "$@" exit $? else @@ -61,7 +61,7 @@ debugFunc() { } flushFunc() { - "${PI_HOLE_SCRIPT_DIR}"/piholeLogFlush.sh + "${PI_HOLE_SCRIPT_DIR}"/piholeLogFlush.sh "$@" exit 0 } @@ -163,16 +163,16 @@ versionFunc() { restartDNS() { dnsmasqPid=$(pidof dnsmasq) - if [[ ${dnsmasqPid} ]]; then - # service already running - reload config - if [ -x "$(command -v systemctl)" ]; then + if [[ "${dnsmasqPid}" ]]; then + # Service already running - reload config + if [[ -x "$(command -v systemctl)" ]]; then systemctl restart dnsmasq else service dnsmasq restart fi else - # service not running, start it up - if [ -x "$(command -v systemctl)" ]; then + # Service not running, start it up + if [[ -x "$(command -v systemctl)" ]]; then systemctl start dnsmasq else service dnsmasq start @@ -181,16 +181,29 @@ restartDNS() { } piholeEnable() { - if [[ "${1}" == "0" ]] ; then - #Disable Pihole + if [[ "${2}" == "-h" ]] || [[ "${2}" == "--help" ]]; then + echo "Usage: pihole disable [time] +Example: 'pihole disable', or 'pihole disable 5m' +Disable Pi-hole subsystems + +Time: + #s Disable Pi-hole functionality for # second(s) + #m Disable Pi-hole functionality for # minute(s)" + exit 0 + elif [[ "${1}" == "0" ]]; then + # Disable Pi-hole sed -i 's/^addn-hosts=\/etc\/pihole\/gravity.list/#addn-hosts=\/etc\/pihole\/gravity.list/' /etc/dnsmasq.d/01-pihole.conf + sed -i 's/^addn-hosts=\/etc\/pihole\/black.list/#addn-hosts=\/etc\/pihole\/black.list/' /etc/dnsmasq.d/01-pihole.conf + if [[ -e "$wildcardlist" ]]; then + mv "$wildcardlist" "/etc/pihole/wildcard.list" + fi echo "::: Blocking has been disabled!" - if [[ $# > 1 ]] ; then - if [[ ${2} == *"s"* ]] ; then + if [[ $# > 1 ]]; then + if [[ "${2}" == *"s"* ]]; then tt=${2%"s"} echo "::: Blocking will be re-enabled in ${tt} seconds" nohup bash -c "sleep ${tt}; pihole enable" /dev/null & - elif [[ ${2} == *"m"* ]] ; then + elif [[ "${2}" == *"m"* ]]; then tt=${2%"m"} echo "::: Blocking will be re-enabled in ${tt} minutes" tt=$((${tt}*60)) @@ -204,24 +217,35 @@ piholeEnable() { fi fi else - #Enable pihole + # Enable Pi-hole echo "::: Blocking has been enabled!" sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf + if [[ -e "/etc/pihole/wildcard.list" ]]; then + mv "/etc/pihole/wildcard.list" "$wildcardlist" + fi fi restartDNS } piholeLogging() { shift + if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then + echo "Usage: pihole logging [options] +Example: 'pihole logging on' +Specify whether the Pi-hole log should be used - if [[ "${1}" == "off" ]] ; then - #Disable Logging +Options: + on Enable the Pi-hole log at /var/log/pihole.log + off Disable the Pi-hole log at /var/log/pihole.log" + exit 0 + elif [[ "${1}" == "off" ]]; then + # Disable logging sed -i 's/^log-queries/#log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^QUERY_LOGGING=true/QUERY_LOGGING=false/' /etc/pihole/setupVars.conf pihole -f echo "::: Logging has been disabled!" - elif [[ "${1}" == "on" ]] ; then - #Enable logging + elif [[ "${1}" == "on" ]]; then + # Enable logging sed -i 's/^#log-queries/log-queries/' /etc/dnsmasq.d/01-pihole.conf sed -i 's/^QUERY_LOGGING=false/QUERY_LOGGING=true/' /etc/pihole/setupVars.conf echo "::: Logging has been enabled!" @@ -233,12 +257,12 @@ piholeLogging() { } piholeStatus() { - if [[ $(netstat -plnt | grep -c ':53 ') > 0 ]]; then - if [[ "${1}" != "web" ]] ; then + if [[ "$(netstat -plnt | grep -c ':53 ')" -gt "0" ]]; then + if [[ "${1}" != "web" ]]; then echo "::: DNS service is running" fi else - if [[ "${1}" == "web" ]] ; then + if [[ "${1}" == "web" ]]; then echo "-1"; else echo "::: DNS service is NOT running" @@ -246,28 +270,28 @@ piholeStatus() { return fi - if [[ $(grep -i "^#addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf) ]] ; then - #list is commented out - if [[ "${1}" == "web" ]] ; then + if [[ "$(grep -i "^#addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then + # List is commented out + if [[ "${1}" == "web" ]]; then echo 0; else echo "::: Pi-hole blocking is Disabled"; fi - elif [[ $(grep -i "^addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf) ]] ; then - #list set - if [[ "${1}" == "web" ]] ; then + elif [[ "$(grep -i "^addn-hosts=/" /etc/dnsmasq.d/01-pihole.conf)" ]]; then + # List set + if [[ "${1}" == "web" ]]; then echo 1; else echo "::: Pi-hole blocking is Enabled"; fi else - #addn-host not found - if [[ "${1}" == "web" ]] ; then + # Addn-host not found + if [[ "${1}" == "web" ]]; then echo 99 else echo "::: No hosts file linked to dnsmasq, adding it in enabled state" fi - #add addn-host= to dnsmasq + # Add addn-host= to dnsmasq echo "addn-hosts=/etc/pihole/gravity.list" >> /etc/dnsmasq.d/01-pihole.conf restartDNS fi @@ -280,46 +304,96 @@ tailFunc() { } piholeCheckoutFunc() { + if [[ "$2" == "-h" ]] || [[ "$2" == "--help" ]]; then + echo "Usage: pihole checkout [repo] [branch] +Example: 'pihole checkout master' or 'pihole checkout core dev' +Switch Pi-hole subsystems to a different Github branch + +Repositories: + core [branch] Change the branch of Pi-hole's core subsystem + web [branch] Change the branch of Admin Console subsystem + +Branches: + master Update subsystems to the latest stable release + dev Update subsystems to the latest development release" + exit 0 + fi + source "${PI_HOLE_SCRIPT_DIR}"/piholeCheckout.sh shift checkout "$@" } +tricorderFunc() { + if [[ ! -p "/dev/stdin" ]]; then + echo "Please do not call Tricorder directly." + exit 1 + fi + + if ! timeout 2 nc -z tricorder.pi-hole.net 9998 &> /dev/null; then + echo "Unable to connect to Pi-hole's Tricorder server." + exit 1 + fi + + if command -v openssl &> /dev/null; then + openssl s_client -quiet -connect tricorder.pi-hole.net:9998 2> /dev/null < /dev/stdin + exit "$?" + else + echo "Your debug log will be transmitted unencrypted via plain-text" + echo "There is a possibility that this could be intercepted by a third party" + echo "If you wish to cancel, press Ctrl-C to exit within 10 seconds" + secs="10" + while [[ "$secs" -gt "0" ]]; do + echo -ne "." + sleep 1 + : $((secs--)) + done + echo " " + nc tricorder.pi-hole.net 9999 < /dev/stdin + exit "$?" + fi +} + helpFunc() { - cat << EOM -::: Control all Pi-hole specific functions -::: -::: Usage: pihole [options] -::: Add -h after -w (whitelist), -b (blacklist), -c (chronometer), or -a (admin) for more information on usage -::: -::: Options: -::: -w, whitelist Whitelist domain(s) -::: -b, blacklist Blacklist domain(s) (exact match) -::: -wild, wildcard Blacklist whole domain(s) (wildcard) -::: -d, debug Start a debugging session -::: Automated debugging can be enabled with '-a'. -::: 'pihole -d -a' -::: -f, flush Flush the 'pihole.log' file -::: -t, tail Output the last lines of the 'pihole.log' file. Lines are appended as the file grows -::: -up, updatePihole Update Pi-hole components -::: -r, reconfigure Reconfigure or Repair Pi-hole -::: -g, updateGravity Update the list of ad-serving domains -::: -c, chronometer Calculates stats and displays to an LCD -::: -h, help Show this help dialog -::: -v, version Show installed versions of Pi-hole and Web-Admin -::: -q, query Query the adlists for a specific domain -::: 'pihole -q domain -exact' shows exact matches only -::: -l, logging Enable or Disable logging (pass 'on' or 'off') -::: -a, admin Admin webpage options -::: uninstall Uninstall Pi-hole from your system! :( -::: status Display if Pi-hole is Enabled or Disabled -::: enable Enable Pi-hole DNS Blocking -::: disable Disable Pi-hole DNS Blocking -::: Blocking can also be disabled only temporarily, e.g., -::: 'pihole disable 5m' - will disable blocking for 5 minutes -::: restartdns Restart dnsmasq -::: checkout Check out different branches -EOM + echo "Usage: pihole [options] +Example: 'pihole -w -h' +Add '-h' after specific commands for more information on usage + +Whitelist/Blacklist Options: + -w, whitelist Whitelist domain(s) + -b, blacklist Blacklist domain(s) + -wild, wildcard Blacklist domain(s), and all its subdomains + Add '-h' for more info on whitelist/blacklist usage + +Debugging Options: + -d, debug Start a debugging session + Add '-a' to enable automated debugging + -f, flush Flush the Pi-hole log + -r, reconfigure Reconfigure or Repair Pi-hole subsystems + -t, tail View the live output of the Pi-hole log + +Options: + -a, admin Admin Console options + Add '-h' for more info on admin console usage + -c, chronometer Calculates stats and displays to an LCD + Add '-h' for more info on chronometer usage + -g, updateGravity Update the list of ad-serving domains + -h, --help, help Show this help dialog + -l, logging Specify whether the Pi-hole log should be used + Add '-h' for more info on logging usage + -q, query Query the adlists for a specified domain + Add '-exact' AFTER a specified domain for exact match + -up, updatePihole Update Pi-hole subsystems + -v, version Show installed versions of Pi-hole, Admin Console & FTL + Add '-h' for more info on version usage + uninstall Uninstall Pi-hole from your system + status Display the running status of Pi-hole subsystems + enable Enable Pi-hole subsystems + disable Disable Pi-hole subsystems + Add '-h' for more info on disable usage + restartdns Restart Pi-hole subsystems + checkout Switch Pi-hole subsystems to a different Github branch + Add '-h' for more info on checkout usage"; exit 0 } @@ -333,7 +407,7 @@ case "${1}" in "-b" | "blacklist" ) blacklistFunc "$@";; "-wild" | "wildcard" ) wildcardFunc "$@";; "-d" | "debug" ) debugFunc "$@";; - "-f" | "flush" ) flushFunc;; + "-f" | "flush" ) flushFunc "$@";; "-up" | "updatePihole" ) updatePiholeFunc;; "-r" | "reconfigure" ) reconfigurePiholeFunc;; "-g" | "updateGravity" ) updateGravityFunc "$@";; @@ -344,11 +418,12 @@ case "${1}" in "-l" | "logging" ) piholeLogging "$@";; "uninstall" ) uninstallFunc;; "enable" ) piholeEnable 1;; - "disable" ) piholeEnable 0 $2;; + "disable" ) piholeEnable 0 "$2";; "status" ) piholeStatus "$2";; "restartdns" ) restartDNS;; "-a" | "admin" ) webpageFunc "$@";; "-t" | "tail" ) tailFunc;; "checkout" ) piholeCheckoutFunc "$@";; + "tricorder" ) tricorderFunc;; * ) helpFunc;; esac diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 8e36fc96..60772625 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -402,6 +402,61 @@ def test_FTL_binary_installed_and_responsive_no_errors(Pihole): # assert '644 /run/pihole-FTL.pid' in support_files.stdout # assert '644 /var/log/pihole-FTL.log' in support_files.stdout +def test_IPv6_only_link_local(Pihole): + ''' confirms IPv6 blocking is disabled for Link-local address ''' + # mock ip -6 address to return Link-local address + mock_command_2('ip', {'-6 address':('inet6 fe80::d210:52fa:fe00:7ad7/64 scope link', '0')}, Pihole) + detectPlatform = Pihole.run(''' + source /opt/pihole/basic-install.sh + useIPv6dialog + ''') + expected_stdout = 'Found neither IPv6 ULA nor GUA address, blocking IPv6 ads will not be enabled' + assert expected_stdout in detectPlatform.stdout + +def test_IPv6_only_ULA(Pihole): + ''' confirms IPv6 blocking is enabled for ULA addresses ''' + # mock ip -6 address to return ULA address + mock_command_2('ip', {'-6 address':('inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole) + detectPlatform = Pihole.run(''' + source /opt/pihole/basic-install.sh + useIPv6dialog + ''') + expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads' + assert expected_stdout in detectPlatform.stdout + +def test_IPv6_only_GUA(Pihole): + ''' confirms IPv6 blocking is enabled for GUA addresses ''' + # mock ip -6 address to return GUA address + mock_command_2('ip', {'-6 address':('inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole) + detectPlatform = Pihole.run(''' + source /opt/pihole/basic-install.sh + useIPv6dialog + ''') + expected_stdout = 'Found IPv6 GUA address, using it for blocking IPv6 ads' + assert expected_stdout in detectPlatform.stdout + +def test_IPv6_GUA_ULA_test(Pihole): + ''' confirms IPv6 blocking is enabled for GUA and ULA addresses ''' + # mock ip -6 address to return GUA and ULA addresses + mock_command_2('ip', {'-6 address':('inet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global\ninet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole) + detectPlatform = Pihole.run(''' + source /opt/pihole/basic-install.sh + useIPv6dialog + ''') + expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads' + assert expected_stdout in detectPlatform.stdout + +def test_IPv6_ULA_GUA_test(Pihole): + ''' confirms IPv6 blocking is enabled for GUA and ULA addresses ''' + # mock ip -6 address to return ULA and GUA addresses + mock_command_2('ip', {'-6 address':('inet6 fda2:2001:5555:0:d210:52fa:fe00:7ad7/64 scope global\ninet6 2003:12:1e43:301:d210:52fa:fe00:7ad7/64 scope global', '0')}, Pihole) + detectPlatform = Pihole.run(''' + source /opt/pihole/basic-install.sh + useIPv6dialog + ''') + expected_stdout = 'Found IPv6 ULA address, using it for blocking IPv6 ads' + assert expected_stdout in detectPlatform.stdout + # Helper functions def mock_command(script, args, container): ''' Allows for setup of commands we don't really want to have to run for real in unit tests ''' @@ -424,6 +479,27 @@ def mock_command(script, args, container): chmod +x {script} rm -f /var/log/{scriptlog}'''.format(script=full_script_path, content=mock_script, scriptlog=script)) +def mock_command_2(script, args, container): + ''' Allows for setup of commands we don't really want to have to run for real in unit tests ''' + full_script_path = '/usr/local/bin/{}'.format(script) + mock_script = dedent('''\ + #!/bin/bash -e + echo "\$0 \$@" >> /var/log/{script} + case "\$1 \$2" in'''.format(script=script)) + for k, v in args.iteritems(): + case = dedent(''' + \"{arg}\") + echo \"{res}\" + exit {retcode} + ;;'''.format(arg=k, res=v[0], retcode=v[1])) + mock_script += case + mock_script += dedent(''' + esac''') + container.run(''' + cat < {script}\n{content}\nEOF + chmod +x {script} + rm -f /var/log/{scriptlog}'''.format(script=full_script_path, content=mock_script, scriptlog=script)) + def run_script(Pihole, script): result = Pihole.run(script) assert result.rc == 0 From 98afb0e9980a716666031206d9955830e9f727f5 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Tue, 27 Jun 2017 14:28:05 +0200 Subject: [PATCH 02/28] pihole checkout ftl --- advanced/Scripts/piholeCheckout.sh | 125 +++++++++++++++++++++++++++-- 1 file changed, 118 insertions(+), 7 deletions(-) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 102db2ba..d4a85e8a 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -24,6 +24,93 @@ update="false" coltable="/opt/pihole/COL_TABLE" source ${coltable} +check_download_exists() { + status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1) + if echo "$status" | grep -q "404"; then + return 1 + else + return 0 + fi +} + +FTLinstall() { + # Download and install FTL binary + local binary="${1}" + local path="${2}" + local str="Installing FTL" + echo -ne " ${INFO} ${str}..." + + if curl -sSL --fail "https://ftl.pi-hole.net/${path}" -o "/tmp/${binary}"; then + # Get sha1 of the binary we just downloaded for verification. + curl -sSL --fail "https://ftl.pi-hole.net/${path}.sha1" -o "/tmp/${binary}.sha1" + # Check if we just downloaded text, or a binary file. + cd /tmp + if sha1sum --status --quiet -c "${binary}".sha1; then + echo -n "transferred... " + stop_service pihole-FTL &> /dev/null + install -T -m 0755 "/tmp/${binary}" "/usr/bin/pihole-FTL" + rm "/tmp/${binary}" "/tmp/${binary}.sha1" + start_service pihole-FTL &> /dev/null + echo -e "${OVER} ${TICK} ${str}" + return 0 + else + echo -e "${OVER} ${CROSS} ${str}" + echo -e " ${COL_LIGHT_RED}Error: Download of binary from ftl.pi-hole.net failed${COL_NC}" + return 1 + fi + else + echo -e "${OVER} ${CROSS} ${str}" + echo -e " ${COL_LIGHT_RED}Error: URL not found${COL_NC}" + fi +} + +get_binary_name() { + local machine + + machine=$(uname -m) + + local str="Detecting architecture" + echo -ne " ${INFO} ${str}..." + if [[ ${machine} == arm* || ${machine} == *aarch* ]]; then + # ARM + local rev=$(uname -m | sed "s/[^0-9]//g;") + local lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') + if [[ "$lib" == "/lib/ld-linux-aarch64.so.1" ]]; then + echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture" + binary="pihole-FTL-aarch64-linux-gnu" + elif [[ "$lib" == "/lib/ld-linux-armhf.so.3" ]]; then + if [ "$rev" -gt "6" ]; then + echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv7+)" + binary="pihole-FTL-arm-linux-gnueabihf" + else + echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv6 or lower) Using ARM binary" + binary="pihole-FTL-arm-linux-gnueabi" + fi + else + echo -e "${OVER} ${TICK} Detected ARM architecture" + binary="pihole-FTL-arm-linux-gnueabi" + fi + elif [[ $machine == ppc ]]; then + # PowerPC + echo "::: Detected PowerPC architecture" + binary="pihole-FTL-powerpc-linux-gnu" + elif [[ ${machine} == x86_64 ]]; then + # 64bit + echo -e "${OVER} ${TICK} Detected x86_64 architecture" + binary="pihole-FTL-linux-x86_64" + else + # Something else - we try to use 32bit executable and warn the user + if [[ ! ${machine} == i686 ]]; then + echo -e "${OVER} ${CROSS} ${str}... + ${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable + Contact support if you experience issues (e.g: FTL not running)${COL_NC}" + else + echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture" + fi + binary="pihole-FTL-linux-x86_32" + fi +} + fully_fetch_repo() { # Add upstream branches to shallow clone local directory="${1}" @@ -50,7 +137,6 @@ get_available_branches() { return } - fetch_checkout_pull_branch() { # Check out specified branch local directory="${1}" @@ -74,7 +160,7 @@ checkout_pull_branch() { cd "${directory}" || return 1 oldbranch="$(git symbolic-ref HEAD)" - + git checkout "${branch}" || return 1 if [[ "$(git diff "${oldbranch}" | grep -c "^")" -gt "0" ]]; then @@ -82,13 +168,13 @@ checkout_pull_branch() { fi git_pull=$(git pull || return 1) - + if [[ "$git_pull" == *"up-to-date"* ]]; then echo -e "\n ${INFO} $(git pull)" else echo -e "$git_pull\n" fi - + return 0 } @@ -150,6 +236,12 @@ checkout() { fetch_checkout_pull_branch "${webInterfaceDir}" "devel" || { echo " ${CROSS} Unable to pull Web development branch"; exit 1; } fi echo -e " ${TICK} Pi-hole core" + + get_binary_name + local path + path="development/${binary}" + FTLinstall "${binary}" "${path}" + elif [[ "${1}" == "master" ]] ; then # Shortcut to check out master branches echo -e " ${INFO} Shortcut \"master\" detected - checking out master branches..." @@ -160,7 +252,12 @@ checkout() { fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo " ${CROSS} Unable to pull Web master branch"; exit 1; } fi echo -e " ${TICK} Web interface" - + + get_binary_name + local path + path="master/${binary}" + FTLinstall "${binary}" "${path}" + elif [[ "${1}" == "core" ]] ; then str="Fetching branches from ${piholeGitUrl}" echo -ne " ${INFO} $str" @@ -196,7 +293,7 @@ checkout() { exit 1 fi webbranches=($(get_available_branches "${webInterfaceDir}")) - + if [[ "${corebranches[@]}" == *"master"* ]]; then echo -e "${OVER} ${TICK} $str ${INFO} ${#webbranches[@]} branches available for Web Admin" @@ -215,13 +312,27 @@ checkout() { exit 1 fi checkout_pull_branch "${webInterfaceDir}" "${2}" + elif [[ "${1}" == "ftl" ]] ; then + + get_binary_name + local path + path="${2}/${binary}" + + if check_download_exists "$path"; then + echo " ${TICK} Branch ${2} exists" + else + echo " ${CROSS} Branch ${2} doesn't exist" + fi + + FTLinstall "${binary}" "${path}" + else echo -e " ${INFO} Requested option \"${1}\" is not available" exit 1 fi # Force updating everything - if [[ ! "${1}" == "web" && "${update}" == "true" ]]; then + if [[ ( ! "${1}" == "web" && ! "${1}" == "ftl" ) && "${update}" == "true" ]]; then echo -e " ${INFO} Running installer to upgrade your installation" if "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" --unattended; then exit 0 From 70fd94edb3ebcfaf16ea9d453e7ca10d7318c9ed Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 5 Jul 2017 19:30:05 +0200 Subject: [PATCH 03/28] Edit message when requested branch of FTL is not available --- advanced/Scripts/piholeCheckout.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index d4a85e8a..7a84f588 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -321,7 +321,7 @@ checkout() { if check_download_exists "$path"; then echo " ${TICK} Branch ${2} exists" else - echo " ${CROSS} Branch ${2} doesn't exist" + echo " ${CROSS} Requested branch \"${2}\" is not available" fi FTLinstall "${binary}" "${path}" From bb8a263d70766863d73d077578ebe041f10793d1 Mon Sep 17 00:00:00 2001 From: DL6ER Date: Wed, 5 Jul 2017 19:34:20 +0200 Subject: [PATCH 04/28] ShellCheck --- advanced/Scripts/piholeCheckout.sh | 38 +++++++++++++++++++----------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index 7a84f588..657bde83 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -35,16 +35,19 @@ check_download_exists() { FTLinstall() { # Download and install FTL binary - local binary="${1}" - local path="${2}" - local str="Installing FTL" + local binary + binary="${1}" + local path + path="${2}" + local str + str="Installing FTL" echo -ne " ${INFO} ${str}..." if curl -sSL --fail "https://ftl.pi-hole.net/${path}" -o "/tmp/${binary}"; then # Get sha1 of the binary we just downloaded for verification. curl -sSL --fail "https://ftl.pi-hole.net/${path}.sha1" -o "/tmp/${binary}.sha1" # Check if we just downloaded text, or a binary file. - cd /tmp + cd /tmp || return 1 if sha1sum --status --quiet -c "${binary}".sha1; then echo -n "transferred... " stop_service pihole-FTL &> /dev/null @@ -66,15 +69,17 @@ FTLinstall() { get_binary_name() { local machine - machine=$(uname -m) - local str="Detecting architecture" + local str + str="Detecting architecture" echo -ne " ${INFO} ${str}..." if [[ ${machine} == arm* || ${machine} == *aarch* ]]; then # ARM - local rev=$(uname -m | sed "s/[^0-9]//g;") - local lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') + local rev + rev=$(uname -m | sed "s/[^0-9]//g;") + local lib + lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') if [[ "$lib" == "/lib/ld-linux-aarch64.so.1" ]]; then echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture" binary="pihole-FTL-aarch64-linux-gnu" @@ -127,7 +132,8 @@ fully_fetch_repo() { get_available_branches() { # Return available branches - local directory="${1}" + local directory + directory="${1}" local output cd "${directory}" || return 1 @@ -139,11 +145,13 @@ get_available_branches() { fetch_checkout_pull_branch() { # Check out specified branch - local directory="${1}" - local branch="${2}" + local directory + directory="${1}" + local branch + branch="${2}" # Set the reference for the requested branch, fetch, check it put and pull it - cd "${directory}" + cd "${directory}" || return 1 git remote set-branches origin "${branch}" || return 1 git stash --all --quiet &> /dev/null || true git clean --force -d || true @@ -153,8 +161,10 @@ fetch_checkout_pull_branch() { checkout_pull_branch() { # Check out specified branch - local directory="${1}" - local branch="${2}" + local directory + directory="${1}" + local branch + branch="${2}" local oldbranch cd "${directory}" || return 1 From 74b912a0b71c1fd2027f4cc0c194fdf636d07dbb Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Wed, 12 Jul 2017 22:02:07 +0100 Subject: [PATCH 05/28] Check if FTL is already installed, do not download if it is detected, and the sha1sum matches the remote This will probably break some tests. I'll work that out in a bit Signed-off-by: Adam Warner Signed-off-by: Adam Warner --- automated install/basic-install.sh | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 74e2a61d..8c026442 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1761,7 +1761,7 @@ FTLinstall() { local binary="${1}" local latesttag local orig_dir - local str="Installing FTL" + local str="Downloading and Installing FTL" echo -ne " ${INFO} ${str}..." # Get the current working directory @@ -1774,6 +1774,7 @@ FTLinstall() { echo -e " ${COL_LIGHT_RED}Error: Unable to get latest release location from GitHub${COL_NC}" return 1 fi + # If the download worked, if curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${latesttag%$'\r'}/${binary}" -o "/tmp/${binary}"; then # get sha1 of the binary we just downloaded for verification. @@ -1879,9 +1880,26 @@ FTLdetect() { binary="pihole-FTL-linux-x86_32" fi - # Install FTL - FTLinstall "${binary}" || return 1 + #In the next section we check to see if FTL is already installed (in case of pihole -r). + #If the installed version matches the latest version, then check the installed sha1sum of the binary vs the remote sha1sum. If they do not match, then download + local FTLversion=$(/usr/bin/pihole-FTL tag) + local FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n') + if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then + # Install FTL + FTLinstall "${binary}" || return 1 + else + local remoteSha1=$(curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${FTLversion%$'\r'}/${binary}.sha1" | cut -d ' ' -f 1) + local localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1) + + echo -e " ${INFO} Existing FTL Binary detected. Checking sha1sum..." + if [[ "${remoteSha1}" != "${localSha1}" ]]; then + echo -e " ${INFO} Corruption detected..." + FTLinstall "${binary}" || return 1 + else + echo -e " ${INFO} sha1sums match. No need to download!" + fi + fi } main() { From bf70c2c6605e15caf433bf5830a7e6a474b22f3a Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Wed, 12 Jul 2017 22:52:03 +0100 Subject: [PATCH 06/28] initial changes to tests to take into account changes to strings Signed-off-by: Adam Warner --- automated install/basic-install.sh | 2 +- test/test_automated_install.py | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 8c026442..3a53fca5 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1817,7 +1817,7 @@ FTLinstall() { # Detect suitable FTL binary platform FTLdetect() { echo "" - echo -e " ${INFO} Downloading latest version of FTL..." + echo -e " ${INFO} FTL Checks..." # Local, named variables local machine diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 60b9dbb8..4a4f72aa 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -319,11 +319,11 @@ def test_FTL_detect_aarch64_no_errors(Pihole): source /opt/pihole/basic-install.sh FTLdetect ''') - expected_stdout = info_box + ' Downloading latest version of FTL...' + expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout expected_stdout = tick_box + ' Detected ARM-aarch64 architecture' assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Installing FTL' + expected_stdout = tick_box + ' Downloading and Installing FTL' assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv6l_no_errors(Pihole): @@ -336,11 +336,11 @@ def test_FTL_detect_armv6l_no_errors(Pihole): source /opt/pihole/basic-install.sh FTLdetect ''') - expected_stdout = info_box + ' Downloading latest version of FTL...' + expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout expected_stdout = tick_box + ' Detected ARM-hf architecture (armv6 or lower)' assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Installing FTL' + expected_stdout = tick_box + ' Downloading and Installing FTL' assert expected_stdout in detectPlatform.stdout def test_FTL_detect_armv7l_no_errors(Pihole): @@ -353,11 +353,11 @@ def test_FTL_detect_armv7l_no_errors(Pihole): source /opt/pihole/basic-install.sh FTLdetect ''') - expected_stdout = info_box + ' Downloading latest version of FTL...' + expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout expected_stdout = tick_box + ' Detected ARM-hf architecture (armv7+)' assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Installing FTL' + expected_stdout = tick_box + ' Downloading and Installing FTL' assert expected_stdout in detectPlatform.stdout def test_FTL_detect_x86_64_no_errors(Pihole): @@ -366,11 +366,11 @@ def test_FTL_detect_x86_64_no_errors(Pihole): source /opt/pihole/basic-install.sh FTLdetect ''') - expected_stdout = info_box + ' Downloading latest version of FTL...' + expected_stdout = info_box + ' FTL Checks...' assert expected_stdout in detectPlatform.stdout expected_stdout = tick_box + ' Detected x86_64 architecture' assert expected_stdout in detectPlatform.stdout - expected_stdout = tick_box + ' Installing FTL' + expected_stdout = tick_box + ' Downloading and Installing FTL' assert expected_stdout in detectPlatform.stdout def test_FTL_detect_unknown_no_errors(Pihole): @@ -391,7 +391,7 @@ def test_FTL_download_aarch64_no_errors(Pihole): source /opt/pihole/basic-install.sh FTLinstall pihole-FTL-aarch64-linux-gnu ''') - expected_stdout = tick_box + ' Installing FTL' + expected_stdout = tick_box + ' Downloading and Installing FTL' assert expected_stdout in download_binary.stdout error = 'Error: Download of binary from Github failed' assert error not in download_binary.stdout @@ -405,7 +405,7 @@ def test_FTL_download_unknown_fails_no_errors(Pihole): source /opt/pihole/basic-install.sh FTLinstall pihole-FTL-mips ''') - expected_stdout = cross_box + ' Installing FTL' + expected_stdout = cross_box + ' Downloading and Installing FTL' assert expected_stdout in download_binary.stdout error = 'Error: URL not found' assert error in download_binary.stdout From e1f818ffb7edc8d7257f40f85d39007a804c7d8c Mon Sep 17 00:00:00 2001 From: Jacob Salmela <4aad0716@opayq.com> Date: Fri, 14 Jul 2017 10:53:45 -0500 Subject: [PATCH 07/28] Tweak/debug improvements (#1585) * check for CIDR notation when comparing IPv6 address to that found in setupVars.conf * highlight bad address entries in pihole.log and reference a corresponding FAQ * show header output if Pi-holes x-header does not match * fix automated mode when running from the dashboard. It would previously not automatically upload and generate a token. * show disclaimer message * undocumented feature for now: obfuscate domains in pihole.log so they are not visible when sent to the Pi-hole developers. We need to make an additonal flag for this in the pihole command. if the variable OBFUSCATE has a value, it will replace the domain in the log with a placeholder value * fix small typo in the disclaimer --- advanced/Scripts/piholeDebug.sh | 68 ++++++++++++++++++++++++++------- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index f98d986a..60b04b73 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -39,6 +39,8 @@ else OVER="\r\033[K" fi +OBFUSCATED_PLACEHOLDER="" + # FAQ URLs for use in showing the debug log FAQ_UPDATE_PI_HOLE="${COL_CYAN}https://discourse.pi-hole.net/t/how-do-i-update-pi-hole/249${COL_NC}" FAQ_CHECKOUT_COMMAND="${COL_CYAN}https://discourse.pi-hole.net/t/the-pihole-command-with-examples/738#checkout${COL_NC}" @@ -47,6 +49,7 @@ FAQ_HARDWARE_REQUIREMENTS_PORTS="${COL_CYAN}https://discourse.pi-hole.net/t/hard FAQ_GATEWAY="${COL_CYAN}https://discourse.pi-hole.net/t/why-is-a-default-gateway-important-for-pi-hole/3546${COL_NC}" FAQ_ULA="${COL_CYAN}https://discourse.pi-hole.net/t/use-ipv6-ula-addresses-for-pi-hole/2127${COL_NC}" FAQ_FTL_COMPATIBILITY="${COL_CYAN}https://github.com/pi-hole/FTL#compatibility-list${COL_NC}" +FAQ_BAD_ADDRESS="${COL_CYAN}https://discourse.pi-hole.net/t/why-do-i-see-bad-address-at-in-pihole-log/3972${COL_NC}" # Other URLs we may use FORUMS_URL="${COL_CYAN}https://discourse.pi-hole.net${COL_NC}" @@ -159,6 +162,17 @@ ${PIHOLE_FTL_LOG} ${PIHOLE_WEB_SERVER_ACCESS_LOG_FILE} ${PIHOLE_WEB_SERVER_ERROR_LOG_FILE}) +DISCLAIMER="This process collects information from your Pi-hole, and optionally uploads it to a unique and random directory on tricorder.pi-hole.net. + +The intent of this script is to allow users to self-diagnose their installations. This is accomplished by running tests against our software and providing the user with links to FAQ articles when a problem is detected. Since we are a small team and Pi-hole has been growing steadily, it is our hope that this will help us spend more time on development. + +NOTE: All log files auto-delete after 48 hours and ONLY the Pi-hole developers can access your data via the given token. We have taken these extra steps to secure your data and will work to further reduce any personal information gathered. +" + +show_disclaimer(){ + log_write "${DISCLAIMER}" +} + source_setup_variables() { # Display the current test that is running log_write "\n${COL_LIGHT_PURPLE}*** [ INITIALIZING ]${COL_NC} Sourcing setup variables" @@ -203,6 +217,7 @@ copy_to_debug_log() { initiate_debug() { # Clear the screen so the debug log is readable clear + show_disclaimer # Display that the debug process is beginning log_write "${COL_LIGHT_PURPLE}*** [ INITIALIZING ]${COL_NC}" # Timestamp the start of the log @@ -457,7 +472,7 @@ does_ip_match_setup_vars() { # If it's an IPv6 address if [[ "${protocol}" == "6" ]]; then # Strip off the / (CIDR notation) - if [[ "${ip_address%/*}" == "${setup_vars_ip}" ]]; then + if [[ "${ip_address%/*}" == "${setup_vars_ip%/*}" ]]; then # if it matches, show it in green log_write " ${COL_LIGHT_GREEN}${ip_address%/*}${COL_NC} matches the IP found in ${PIHOLE_SETUP_VARS_FILE}" else @@ -659,6 +674,10 @@ check_x_headers() { block_page_working="X-Pi-hole: A black hole for Internet advertisements." local dashboard_working dashboard_working="X-Pi-hole: The Pi-hole Web interface is working!" + local full_curl_output_block_page + full_curl_output_block_page="$(curl -Is localhost)" + local full_curl_output_dashboard + full_curl_output_dashboard="$(curl -Is localhost/admin/)" # If the X-header found by curl matches what is should be, if [[ $block_page == "$block_page_working" ]]; then # display a success message @@ -666,6 +685,7 @@ check_x_headers() { else # Otherwise, show an error log_write "$CROSS ${COL_LIGHT_RED}X-Header does not match or could not be retrieved.${COL_NC}" + log_write "${COL_LIGHT_RED}${full_curl_output_block_page}${COL_NC}" fi # Same logic applies to the dashbord as above, if the X-Header matches what a working system shoud have, @@ -675,6 +695,7 @@ check_x_headers() { else # Othewise, it's a failure since the X-Headers either don't exist or have been modified in some way log_write "$CROSS ${COL_LIGHT_RED}X-Header does not match or could not be retrieved.${COL_NC}" + log_write "${COL_LIGHT_RED}${full_curl_output_dashboard}${COL_NC}" fi } @@ -972,8 +993,39 @@ analyze_pihole_log() { local pihole_log_head=() pihole_log_head=( $(head -n 20 ${PIHOLE_LOG}) ) log_write " ${COL_CYAN}-----head of $(basename ${PIHOLE_LOG})------${COL_NC}" + local error_to_check_for + local line_to_obfuscate + local obfuscated_line for head_line in "${pihole_log_head[@]}"; do - log_write " ${head_line}" + # A common error in the pihole.log is when there is a non-hosts formatted file + # that the DNS server is attempting to read. Since it's not formatted + # correctly, there will be an entry for "bad address at line n" + # So we can check for that here and highlight it in red so the user can see it easily + error_to_check_for=$(echo ${head_line} | grep 'bad address at') + # Some users may not want to have the domains they visit sent to us + # To that end, we check for lines in the log that would contain a domain name + line_to_obfuscate=$(echo ${head_line} | grep ': query\|: forwarded\|: reply') + # If the variable contains a value, it found an error in the log + if [[ -n ${error_to_check_for} ]]; then + # So we can print it in red to make it visible to the user + log_write " ${CROSS} ${COL_LIGHT_RED}${head_line}${COL_NC} (${FAQ_BAD_ADDRESS})" + else + # If the variable does not a value (the current default behavior), so do not obfuscate anything + if [[ -z ${OBFUSCATE} ]]; then + log_write " ${head_line}" + # Othwerise, a flag was passed to this command to obfuscate domains in the log + else + # So first check if there are domains in the log that should be obfuscated + if [[ -n ${line_to_obfuscate} ]]; then + # If there are, we need to use awk to replace only the domain name (the 6th field in the log) + # so we substitue the domain for the placeholder value + obfuscated_line=$(echo ${line_to_obfuscate} | awk -v placeholder="${OBFUSCATED_PLACEHOLDER}" '{sub($6,placeholder); print $0}') + log_write " ${obfuscated_line}" + else + log_write " ${head_line}" + fi + fi + fi done log_write "" # Set the IFS back to what it was @@ -1019,17 +1071,7 @@ upload_to_tricorder() { # let the user know log_write "${INFO} Debug script running in automated mode" # and then decide again which tool to use to submit it - if command -v openssl &> /dev/null; then - # If openssl is available, use it - log_write "${INFO} Using ${COL_LIGHT_GREEN}openssl${COL_NC} for transmission." - # Save the token returned by our server in a variable - tricorder_token=$(openssl s_client -quiet -connect tricorder.pi-hole.net:${TRICORDER_SSL_PORT_NUMBER} 2> /dev/null < /dev/stdin) - else - # Otherwise, fallback to netcat - log_write "${INFO} Using ${COL_YELLOW}netcat${COL_NC} for transmission." - # Save the token returned by our server in a variable - tricorder_token=$(nc tricorder.pi-hole.net ${TRICORDER_NC_PORT_NUMBER} < /dev/stdin) - fi + tricorder_use_nc_or_ssl # If we're not running in automated mode, else echo "" From 7d56e2a9371e952b5ffea24a298615a73e098b11 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Fri, 14 Jul 2017 20:54:41 +0100 Subject: [PATCH 08/28] `FTL` has an `L` in it, innit? Signed-off-by: Adam Warner --- advanced/Scripts/piholeDebug.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/piholeDebug.sh b/advanced/Scripts/piholeDebug.sh index 60b04b73..ea387b5a 100755 --- a/advanced/Scripts/piholeDebug.sh +++ b/advanced/Scripts/piholeDebug.sh @@ -598,7 +598,7 @@ compare_port_to_service_assigned() { # The programs we use may change at some point, so they are in a varible here local resolver="dnsmasq" local web_server="lighttpd" - local ftl="pihole-FT" + local ftl="pihole-FTL" if [[ "${service_name}" == "${resolver}" ]] || [[ "${service_name}" == "${web_server}" ]] || [[ "${service_name}" == "${ftl}" ]]; then # if port 53 is dnsmasq, show it in green as it's standard log_write "[${COL_LIGHT_GREEN}${port_number}${COL_NC}] is in use by ${COL_LIGHT_GREEN}${service_name}${COL_NC}" @@ -615,7 +615,7 @@ check_required_ports() { # so we can detect any issues local resolver="dnsmasq" local web_server="lighttpd" - local ftl="pihole-FT" + local ftl="pihole-FTL" # Create an array for these ports in use ports_in_use=() # Sort the addresses and remove duplicates From 9464b71a6e37a4067932362fd57f367ef7872d2b Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Mon, 26 Jun 2017 09:51:21 -0400 Subject: [PATCH 09/28] Remove /* from IPv6 as well as IPv4 Fixes Discourse issue: https://discourse.pi-hole.net/t/ipv6-aaaa-dns-issue/3830 Signed-off-by: Adam Warner # Conflicts: # gravity.sh --- gravity.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gravity.sh b/gravity.sh index 285ce5c3..41e3c68a 100755 --- a/gravity.sh +++ b/gravity.sh @@ -46,9 +46,9 @@ else exit 1 fi -#Remove the /* from the end of the IPv4addr. +#Remove the /* from the end of the IP addresses IPV4_ADDRESS=${IPV4_ADDRESS%/*} -IPV6_ADDRESS=${IPV6_ADDRESS} +IPV6_ADDRESS=${IPV6_ADDRESS%/*} # Variables for various stages of downloading and formatting the list basename=pihole From edb594461d3ac683934eb9b256d93946f92823ad Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Wed, 28 Jun 2017 22:54:02 +0100 Subject: [PATCH 10/28] Remove CIDR from IPv6 address when detecting it in the install script Signed-off-by: Adam Warner # Conflicts: # automated install/basic-install.sh --- automated install/basic-install.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 053e04ba..2cf2c61d 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -344,8 +344,8 @@ useIPv6dialog() { # Determine type of found IPv6 addresses for i in "${IPV6_ADDRESSES[@]}"; do result=$(testIPv6 "$i") - [[ "${result}" == "ULA" ]] && ULA_ADDRESS="$i" - [[ "${result}" == "GUA" ]] && GUA_ADDRESS="$i" + [[ "${result}" == "ULA" ]] && ULA_ADDRESS="${i%/*}" + [[ "${result}" == "GUA" ]] && GUA_ADDRESS="${i%/*}" done # Determine which address to be used: Prefer ULA over GUA or don't use any if none found @@ -1510,10 +1510,9 @@ main() { else echo "::: Update complete!" fi - if [[ ${INSTALL_WEB} == true ]]; then if (( ${#pw} > 0 )) ; then - echo ":::" + echo ":::" echo "::: Note: As security measure a password has been installed for your web interface" echo "::: The currently set password is" echo "::: ${pw}" @@ -1524,7 +1523,8 @@ main() { fi echo ":::" - echo "::: The install log is located at: /etc/pihole/install.log" + echo "::: The install log is located at: /etc/pihole/install.log + " } if [[ "${PH_TEST}" != true ]] ; then From 9101916719a86ab9478496bf18e4e0baa42ab3ab Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sat, 24 Jun 2017 15:49:16 -0700 Subject: [PATCH 11/28] Test exclusion for sourced files SC1090. Signed-off-by: Dan Schaper --- test/test_shellcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_shellcheck.py b/test/test_shellcheck.py index fa342027..9c4f141b 100644 --- a/test/test_shellcheck.py +++ b/test/test_shellcheck.py @@ -7,7 +7,7 @@ run_local = testinfra.get_backend( def test_scripts_pass_shellcheck(): ''' Make sure shellcheck does not find anything wrong with our shell scripts ''' - shellcheck = "find . -type f \( -name 'update.sh' -o -name 'piholeDebug.sh' \) | while read file; do shellcheck \"$file\"; done;" + shellcheck = "find . -type f \( -name 'update.sh' -o -name 'piholeDebug.sh' \) | while read file; do shellcheck \"$file\" -e SC1090; done;" results = run_local(shellcheck) print results.stdout assert '' == results.stdout From 66f32b7601d52266e7f244cda3f9334f06d5e6ce Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sun, 25 Jun 2017 12:26:16 -0700 Subject: [PATCH 12/28] Remove testing for `debug` as this is getting a re-write by Jacob. Signed-off-by: Dan Schaper --- test/test_shellcheck.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_shellcheck.py b/test/test_shellcheck.py index 9c4f141b..1b6b35a7 100644 --- a/test/test_shellcheck.py +++ b/test/test_shellcheck.py @@ -7,7 +7,7 @@ run_local = testinfra.get_backend( def test_scripts_pass_shellcheck(): ''' Make sure shellcheck does not find anything wrong with our shell scripts ''' - shellcheck = "find . -type f \( -name 'update.sh' -o -name 'piholeDebug.sh' \) | while read file; do shellcheck \"$file\" -e SC1090; done;" + shellcheck = "find . -type f -name 'update.sh' | while read file; do shellcheck \"$file\" -e SC1090; done;" results = run_local(shellcheck) print results.stdout assert '' == results.stdout From 05c8687041be34065724fd2e33783a47feae10ff Mon Sep 17 00:00:00 2001 From: Dan Schaper Date: Sun, 25 Jun 2017 14:21:41 -0700 Subject: [PATCH 13/28] Integrate DL's update.sh fixes. `shellcheck -x` for following files. Do not test for included files SC1091 Signed-off-by: Dan Schaper Signed-off-by: Adam Warner # Conflicts: # advanced/Scripts/update.sh --- advanced/Scripts/update.sh | 23 ++++++++++++++++------- test/test_shellcheck.py | 2 +- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 6aef183b..e3a7f8fd 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -19,8 +19,14 @@ readonly ADMIN_INTERFACE_DIR="/var/www/html/admin" readonly PI_HOLE_GIT_URL="https://github.com/pi-hole/pi-hole.git" readonly PI_HOLE_FILES_DIR="/etc/.pihole" +# shellcheck disable=SC2034 PH_TEST=true -source ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh + +# Have to ignore the following rule as spaces in paths are not supported by ShellCheck +#shellcheck disable=SC1090 +source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" + +source "/opt/pihole/COL_TABLE" # is_repo() sourced from basic-install.sh # make_repo() sourced from basic-install.sh @@ -30,14 +36,14 @@ source ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh GitCheckUpdateAvail() { local directory="${1}" curdir=$PWD - cd "${directory}" + cd "${directory}" || return # Fetch latest changes in this repo git fetch --quiet origin # @ alone is a shortcut for HEAD. Older versions of git # need @{0} - LOCAL="$(git rev-parse @{0})" + LOCAL="$("git rev-parse @{0}")" # The suffix @{upstream} to a branchname # (short form @{u}) refers @@ -46,7 +52,7 @@ GitCheckUpdateAvail() { # (configured with branch..remote and # branch..merge). A missing branchname # defaults to the current one. - REMOTE="$(git rev-parse @{upstream})" + REMOTE="$("git rev-parse @{upstream}")" if [[ ${#LOCAL} == 0 ]]; then echo "::: Error: Local revision could not be obtained, ask Pi-hole support." @@ -62,7 +68,7 @@ GitCheckUpdateAvail() { fi # Change back to original directory - cd "${curdir}" + cd "${curdir}" || exit if [[ "${LOCAL}" != "${REMOTE}" ]]; then # Local branch is behind remote branch -> Update @@ -77,8 +83,10 @@ GitCheckUpdateAvail() { FTLcheckUpdate() { - local FTLversion=$(/usr/bin/pihole-FTL tag) - local FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n') + local FTLversion + FTLversion=$(/usr/bin/pihole-FTL tag) + local FTLlatesttag + FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n') if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then return 0 @@ -90,6 +98,7 @@ FTLcheckUpdate() { main() { local pihole_version_current local web_version_current + #shellcheck disable=1090,2154 source "${setupVars}" #This is unlikely diff --git a/test/test_shellcheck.py b/test/test_shellcheck.py index 1b6b35a7..5b1a8961 100644 --- a/test/test_shellcheck.py +++ b/test/test_shellcheck.py @@ -7,7 +7,7 @@ run_local = testinfra.get_backend( def test_scripts_pass_shellcheck(): ''' Make sure shellcheck does not find anything wrong with our shell scripts ''' - shellcheck = "find . -type f -name 'update.sh' | while read file; do shellcheck \"$file\" -e SC1090; done;" + shellcheck = "find . -type f -name 'update.sh' | while read file; do shellcheck -x \"$file\" -e SC1090,SC1091; done;" results = run_local(shellcheck) print results.stdout assert '' == results.stdout From 3631d1349ebe06ad804df8ca4ba008ca1b917433 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Sat, 15 Jul 2017 20:11:06 +1000 Subject: [PATCH 14/28] Prevent Web Admin from printing restartdns colour codes (#1575) * Prevent Web Admin from printing unnecessary msgs * Make DNS restart behaviour consistent --- advanced/Scripts/webpage.sh | 17 +++++++++-------- pihole | 24 ++++++++++++++++-------- 2 files changed, 25 insertions(+), 16 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 5aae18f7..42272122 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -221,18 +221,19 @@ Reboot() { } RestartDNS() { - local str="Restarting dnsmasq" - echo -ne " ${INFO} ${str}..." - if [[ -x "$(command -v systemctl)" ]]; then - systemctl restart dnsmasq + local str="Restarting DNS service" + [[ -t 1 ]] && echo -ne " ${INFO} ${str}" + if command -v systemctl &> /dev/null; then + output=$( { systemctl restart dnsmasq; } 2>&1 ) else - service dnsmasq restart + output=$( { service dnsmasq restart; } 2>&1 ) fi - if [[ "$?" == 0 ]]; then - echo -e "${OVER} ${TICK} ${str}" + if [[ -z "${output}" ]]; then + [[ -t 1 ]] && echo -e "${OVER} ${TICK} ${str}" else - echo -e "${OVER} ${CROSS} ${str}" + [[ ! -t 1 ]] && OVER="" + echo -e "${OVER} ${CROSS} ${output}" fi } diff --git a/pihole b/pihole index 3c321b93..b4b5e886 100755 --- a/pihole +++ b/pihole @@ -173,24 +173,32 @@ versionFunc() { restartDNS() { dnsmasqPid=$(pidof dnsmasq) + local str="Restarting DNS service" + echo -ne " ${INFO} ${str}" if [[ "${dnsmasqPid}" ]]; then # Service already running - reload config - echo -ne " ${INFO} Restarting dnsmasq" if [[ -x "$(command -v systemctl)" ]]; then - systemctl restart dnsmasq + output=$( { systemctl restart dnsmasq; } 2>&1 ) else - service dnsmasq restart + output=$( { service dnsmasq restart; } 2>&1 ) + fi + if [[ -z "${output}" ]]; then + echo -e "${OVER} ${TICK} ${str}" + else + echo -e "${OVER} ${CROSS} ${output}" fi - [[ "$?" == 0 ]] && echo -e "${OVER} ${TICK} Restarted dnsmasq" || echo -e "${OVER} ${CROSS} Failed to restart dnsmasq" else # Service not running, start it up - echo -ne " ${INFO} Starting dnsmasq" if [[ -x "$(command -v systemctl)" ]]; then - systemctl start dnsmasq + output=$( { systemctl start dnsmasq; } 2>&1 ) else - service dnsmasq start + output=$( { service dnsmasq start; } 2>&1 ) + fi + if [[ -z "${output}" ]]; then + echo -e "${OVER} ${TICK} ${str}" + else + echo -e "${OVER} ${CROSS} ${output}" fi - [[ "$?" == 0 ]] && echo -e "${OVER} ${TICK} Restarted dnsmasq" || echo -e "${OVER} ${CROSS} Failed to restart dnsmasq" fi } From 3a50b91722ed438e7db86e2b0a147b91642116c8 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Sat, 15 Jul 2017 20:56:40 +1000 Subject: [PATCH 15/28] User-friendly queryFunc() output (#1483) * User-friendly queryFunc() output * Silence grep errors * Provide 'pihole -q -h' help output * Rewrite option handling * Loop through grep stdout to make query output user friendly * Add -adlist option to show block list URL instead of internal file name * Limit general searches to 10 matches per block list * Add -all option to override 10 match limit * Fixed 'pihole -h' wording * Further query optimisations * Optimised scanList() output by switching folder * Re-added processWildcards() function * Added "-bp" exact matching option for use with block page * Standardised query output * Separated wildcard search from blacklist/whitelist search * Optimised sorting by sorting glob output and not scanList() output * Fixed result skipping * Add text for wildcard result on exact query * Fix wildcard result output * Multiple wildcard matches on exact query could cause unexpected output * Remove unnecessary replacement * Make grep only output matching text * HOSTS format lists will also output the IP address * That substitution was necessary * Remove IP address from HOSTS format lists * Filter unwanted content * Add /dev/null to grep, to always print file name (even when searching only one block list) * Use three seds to remove unwanted content from block lists * Merge with development * Simplify queryFunc code --- pihole | 244 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 206 insertions(+), 38 deletions(-) diff --git a/pihole b/pihole index b4b5e886..1bd35dbb 100755 --- a/pihole +++ b/pihole @@ -87,10 +87,14 @@ scanList(){ domain="${1}" list="${2}" method="${3}" - if [[ ${method} == "-exact" ]] ; then - grep -i -E "(^|\s)${domain}($|\s)" "${list}" + + # Switch folder, preventing grep from printing file path + cd "/etc/pihole" || return 1 + + if [[ -n "${method}" ]]; then + grep -i -E -l "(^|\s|\/)${domain}($|\s|\/)" ${list} /dev/null 2> /dev/null else - grep -i "${domain}" "${list}" + grep -i "${domain}" ${list} /dev/null 2> /dev/null fi } @@ -110,46 +114,210 @@ processWildcards() { } queryFunc() { - domain="${2}" - - if [[ -z "${domain}" ]]; then - echo -e " ${COL_LIGHT_RED}Invalid option${COL_NC} - Try 'pihole query --help' for more information." + options="$*" + options="${options/-q /}" + + if [[ "${options}" == "-h" ]] || [[ "${options}" == "--help" ]]; then + echo "Usage: pihole -q [option] +Example: 'pihole -q -exact domain.com' +Query the adlists for a specified domain + +Options: + -adlist Print the name of the block list URL + -exact Search the block lists for exact domain matches + -all Return all query matches within a block list + -h, --help Show this help dialog" + exit 0 + fi + + if [[ "${options}" == *"-exact"* ]]; then + method="exact" + exact=true + fi + + if [[ "${options}" == *"-adlist"* ]]; then + adlist=true + fi + + if [[ "${options}" == *"-bp"* ]]; then + method="exact" + blockpage=true + fi + + if [[ "${options}" == *"-all"* ]]; then + all=true + fi + + # Strip valid options, leaving only the domain and invalid options + options=$(sed 's/ \?-\(exact\|adlist\|bp\|all\) \?//g' <<< "$options") + + # Handle errors + if [[ "${options}" == *" "* ]]; then + error=true + str="Unknown option specified" + elif [[ "${options}" == "-q" ]]; then + error=true + str="No domain specified" + fi + + if [[ -n "${error}" ]]; then + echo -e " ${COL_LIGHT_RED}${str}${COL_NC} + Try 'pihole -q --help' for more information." exit 1 fi - - method="${3}" - lists=( /etc/pihole/list.* /etc/pihole/blacklist.txt) - for list in ${lists[@]}; do - if [ -e "${list}" ]; then - result=$(scanList ${domain} ${list} ${method}) - # Remove empty lines before couting number of results - count=$(sed '/^\s*$/d' <<< "$result" | wc -l) - echo "${list} (${count} results)" - if [[ ${count} > 0 ]]; then - echo "${result}" - fi - echo "" - else - echo -e " ${CROSS} List does not exist" - echo "" - fi - done - # Scan for possible wildcard matches - if [ -e "${wildcardlist}" ]; then - local wildcards=($(processWildcards "${domain}")) - for domain in ${wildcards[@]}; do - result=$(scanList "\/${domain}\/" ${wildcardlist}) - # Remove empty lines before couting number of results - count=$(sed '/^\s*$/d' <<< "$result" | wc -l) - if [[ ${count} > 0 ]]; then - echo -e " ${TICK} Wildcard blocking ${domain} (${count} results)" - echo "${result}" - echo "" + # If domain contains non ASCII characters, convert domain to punycode if python is available + # Cr: https://serverfault.com/a/335079 + if [[ "$options" = *[![:ascii:]]* ]]; then + if command -v python &> /dev/null; then + query=$(python -c 'import sys;print sys.argv[1].decode("utf-8").encode("idna")' "${options}") + fi + else + query="${options}" + fi + + # Scan Whitelist and Blacklist + lists="whitelist.txt blacklist.txt" + results=($(scanList "${query}" "${lists}" "${method}")) + + if [[ -n "${results[*]}" ]]; then + # Loop through each scanList line to print appropriate title + for result in "${results[@]}"; do + filename="${result/:*/}" + if [[ -n "$exact" ]]; then + printf " Exact result in %s\n" "${filename}" + elif [[ -n "$blockpage" ]]; then + printf " [i] %s\n" "${filename}" + else + domain="${result/*:/}" + if [[ ! "${filename}" == "${filename_prev:-}" ]]; then + printf " Result from %s\n" "${filename}" + fi + printf " %s\n" "${domain}" + filename_prev="${filename}" fi done fi + + # Scan Wildcards + if [[ -e "${wildcardlist}" ]]; then + wildcards=($(processWildcards "${query}")) + + for match in "${wildcards[@]}"; do + results=($(scanList "\/${match}\/" ${wildcardlist})) + + if [[ -n "${results[*]}" ]]; then + # Remove empty lines before couting number of results + count=$(sed '/^\s*$/d' <<< "${results[@]}" | wc -l) + if [[ "${count}" -ge 0 ]]; then + blResult=true + if [[ -z "${blockpage}" ]]; then + printf " Wildcard result in %s\n" "${wildcardlist/*dnsmasq.d\/}" + fi + + if [[ -n "${blockpage}" ]]; then + echo " ${INFO} ${match}" + else + echo " *.${match}" + fi + fi + fi + done + + [[ -n "${blResult}" ]] && [[ -n "${blockpage}" ]] && exit 0 + fi + + # Glob *.domains file names, remove file paths and sort by list number + lists_raw=(/etc/pihole/*.domains) + IFS_OLD=$IFS + IFS=$'\n' + lists=$(sort -t . -k 2 -g <<< "${lists_raw[*]//\/etc\/pihole\//}") + + # Scan Domains files + results=($(scanList "${query}" "${lists}" "${method}")) + + # Handle notices + if [[ -z "${blResult}" ]] && [[ -z "${results[*]}" ]]; then + notice=true + str="No ${method/t/t }results found for ${query} found within block lists" + elif [[ -z "${all}" ]] && [[ "${#results[*]}" -ge 16000 ]]; then + # 16000 chars is 15 chars X 1000 lines worth of results + notice=true + str="Hundreds of ${method/t/t }results found for ${query} + This can be overriden using the -all option" + fi + + if [[ -n "${notice}" ]]; then + echo -e " ${INFO} ${str}" + exit + fi + + # Remove unwanted content from results + if [[ -z "${method}" ]]; then + results=($(sed "/:#/d" <<< "${results[*]}")) # Lines starting with comments + results=($(sed "s/[ \t]#.*//g" <<< "${results[*]}")) # Comments after domain + results=($(sed "s/:.*[ \t]/:/g" <<< "${results[*]}")) # IP address + fi + IFS=$IFS_OLD + + # Get adlist content as array + if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then + if [[ -f "/etc/pihole/adlists.list" ]]; then + for url in $(< /etc/pihole/adlists.list); do + if [[ "${url:0:4}" == "http" ]] || [[ "${url:0:3}" == "www" ]]; then + adlists+=("$url") + fi + done + else + echo -e " ${COL_LIGHT_RED}The file '/etc/pihole/adlists.list' was not found${COL_NC}" + exit 1 + fi + fi + + if [[ -n "${results[*]}" ]]; then + if [[ -n "${exact}" ]]; then + echo " Exact result(s) for ${query} found in:" + fi + + for result in "${results[@]}"; do + filename="${result/:*/}" + + # Convert file name to URL name for -adlist or -bp options + if [[ -n "${adlist}" ]] || [[ -n "${blockpage}" ]]; then + filenum=("${filename/list./}") + filenum=("${filenum/.*/}") + filename="${adlists[$filenum]}" + fi + + if [[ -n "${exact}" ]]; then + printf " %s\n" "${filename}" + elif [[ -n "${blockpage}" ]]; then + printf " [%s] %s\n" "${filenum}" "${filename}" + else # Standard query output + + # Print filename heading once per file, not for every match + if [[ ! "${filename}" == "${filename_prev:-}" ]]; then + unset count + printf " Result from %s\n" "${filename}" + else + let count++ + fi + + # Print matching domain if $max_count has not been reached + [[ -z "${all}" ]] && max_count="20" + if [[ -z "${all}" ]] && [[ "${count}" -eq "${max_count}" ]]; then + echo " Over $count results found, skipping rest of file" + elif [[ -z "${all}" ]] && [[ "${count}" -gt "${max_count}" ]]; then + continue + else + domain="${result/*:/}" + printf " %s\n" "${domain}" + fi + filename_prev="${filename}" + fi + done + fi + exit 0 } @@ -438,7 +606,7 @@ Options: -l, logging Specify whether the Pi-hole log should be used Add '-h' for more info on logging usage -q, query Query the adlists for a specified domain - Add '-exact' AFTER a specified domain for exact match + Add '-h' for more info on query usage -up, updatePihole Update Pi-hole subsystems -v, version Show installed versions of Pi-hole, Admin Console & FTL Add '-h' for more info on version usage From 8af9853b9a516367031178ba836246f196e1ef55 Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Sun, 16 Jul 2017 22:32:34 +1000 Subject: [PATCH 16/28] Fixed minor formatting issues * Removed useless echo * Quoted and braced conditionals * Explicit escaping of newline * Fixed arrays implicitly concatenating (SC2199) * Fixed incorrect variable used in checkout web --- advanced/Scripts/piholeCheckout.sh | 39 +++++++++++++++--------------- 1 file changed, 19 insertions(+), 20 deletions(-) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index f9add489..44058111 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -26,7 +26,7 @@ source ${coltable} check_download_exists() { status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1) - if echo "$status" | grep -q "404"; then + if grep -q "404" <<< "$status"; then return 1 else return 0 @@ -74,17 +74,17 @@ get_binary_name() { local str str="Detecting architecture" echo -ne " ${INFO} ${str}..." - if [[ ${machine} == arm* || ${machine} == *aarch* ]]; then + if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then # ARM local rev rev=$(uname -m | sed "s/[^0-9]//g;") local lib lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') - if [[ "$lib" == "/lib/ld-linux-aarch64.so.1" ]]; then + if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture" binary="pihole-FTL-aarch64-linux-gnu" - elif [[ "$lib" == "/lib/ld-linux-armhf.so.3" ]]; then - if [ "$rev" -gt "6" ]; then + elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then + if [[ "$rev" -gt "6" ]]; then echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv7+)" binary="pihole-FTL-arm-linux-gnueabihf" else @@ -95,17 +95,17 @@ get_binary_name() { echo -e "${OVER} ${TICK} Detected ARM architecture" binary="pihole-FTL-arm-linux-gnueabi" fi - elif [[ $machine == ppc ]]; then + elif [[ "${machine}" == "ppc" ]]; then # PowerPC - echo "::: Detected PowerPC architecture" + echo -e "${OVER} ${TICK} Detected PowerPC architecture" binary="pihole-FTL-powerpc-linux-gnu" - elif [[ ${machine} == x86_64 ]]; then + elif [[ "${machine}" == "x86_64" ]]; then # 64bit echo -e "${OVER} ${TICK} Detected x86_64 architecture" binary="pihole-FTL-linux-x86_64" else # Something else - we try to use 32bit executable and warn the user - if [[ ! ${machine} == i686 ]]; then + if [[ ! "${machine}" == "i686" ]]; then echo -e "${OVER} ${CROSS} ${str}... ${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable Contact support if you experience issues (e.g: FTL not running)${COL_NC}" @@ -182,7 +182,7 @@ checkout_pull_branch() { if [[ "$git_pull" == *"up-to-date"* ]]; then echo -e " ${INFO} $(git pull)" else - echo -e "$git_pull\n" + echo -e "$git_pull\\n" fi return 0 @@ -193,13 +193,13 @@ warning1() { 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}" read -r -p " Have you read and understood this? [y/N] " response - case ${response} in + case "${response}" in [yY][eE][sS]|[yY]) echo "" return 0 ;; *) - echo -e "\n ${INFO} Branch change has been cancelled" + echo -e "\\n ${INFO} Branch change has been cancelled" return 1 ;; esac @@ -218,7 +218,7 @@ checkout() { Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}" exit 1; fi - if [[ ${INSTALL_WEB} == "true" ]]; then + if [[ "${INSTALL_WEB}" == "true" ]]; then if ! is_repo "${webInterfaceDir}" ; then echo -e " ${COL_LIGHT_RED}Error: Web Admin repo is missing from system! Please re-run install script from https://github.com/pi-hole/pi-hole${COL_NC}" @@ -242,7 +242,7 @@ checkout() { echo "" echo -e " ${INFO} Pi-hole Core" fetch_checkout_pull_branch "${PI_HOLE_FILES_DIR}" "development" || { echo " ${CROSS} Unable to pull Core developement branch"; exit 1; } - if [[ ${INSTALL_WEB} == "true" ]]; then + if [[ "${INSTALL_WEB}" == "true" ]]; then echo "" echo -e " ${INFO} Web interface" fetch_checkout_pull_branch "${webInterfaceDir}" "devel" || { echo " ${CROSS} Unable to pull Web development branch"; exit 1; } @@ -263,7 +263,7 @@ checkout() { fetch_checkout_pull_branch "${webInterfaceDir}" "master" || { echo " ${CROSS} Unable to pull Web master branch"; exit 1; } fi #echo -e " ${TICK} Web Interface" - get_binary_name + get_binary_name local path path="master/${binary}" FTLinstall "${binary}" "${path}" @@ -276,12 +276,12 @@ checkout() { fi corebranches=($(get_available_branches "${PI_HOLE_FILES_DIR}")) - if [[ "${corebranches[@]}" == *"master"* ]]; then + if [[ "${corebranches[*]}" == *"master"* ]]; then echo -e "${OVER} ${TICK} $str ${INFO} ${#corebranches[@]} branches available for Pi-hole Core" else # Print STDERR output from get_available_branches - echo -e "${OVER} ${CROSS} $str\n\n${corebranches[*]}" + echo -e "${OVER} ${CROSS} $str\\n\\n${corebranches[*]}" exit 1 fi @@ -303,12 +303,12 @@ checkout() { fi webbranches=($(get_available_branches "${webInterfaceDir}")) - if [[ "${corebranches[@]}" == *"master"* ]]; then + if [[ "${webbranches[*]}" == *"master"* ]]; then echo -e "${OVER} ${TICK} $str ${INFO} ${#webbranches[@]} branches available for Web Admin" else # Print STDERR output from get_available_branches - echo -e "${OVER} ${CROSS} $str\n\n${corebranches[*]}" + echo -e "${OVER} ${CROSS} $str\\n\\n${webbranches[*]}" exit 1 fi @@ -322,7 +322,6 @@ checkout() { fi checkout_pull_branch "${webInterfaceDir}" "${2}" elif [[ "${1}" == "ftl" ]] ; then - get_binary_name local path path="${2}/${binary}" From f2e9d585f79f757302eaf7417f6e7209bc848352 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 16 Jul 2017 13:22:59 +0100 Subject: [PATCH 17/28] * Add helptext for `pihole checkout ftl` * Only attempt to install FTL if branch was found * ~~corebranches~~ webbranches (web branches now actually listed) Signed-off-by: Adam Warner --- advanced/Scripts/piholeCheckout.sh | 7 +++---- pihole | 7 ++++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index f9add489..b786ba0c 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -303,12 +303,12 @@ checkout() { fi webbranches=($(get_available_branches "${webInterfaceDir}")) - if [[ "${corebranches[@]}" == *"master"* ]]; then + if [[ "${webbranches[@]}" == *"master"* ]]; then echo -e "${OVER} ${TICK} $str ${INFO} ${#webbranches[@]} branches available for Web Admin" else # Print STDERR output from get_available_branches - echo -e "${OVER} ${CROSS} $str\n\n${corebranches[*]}" + echo -e "${OVER} ${CROSS} $str\n\n${webbranches[*]}" exit 1 fi @@ -329,12 +329,11 @@ checkout() { if check_download_exists "$path"; then echo " ${TICK} Branch ${2} exists" + FTLinstall "${binary}" "${path}" else echo " ${CROSS} Requested branch \"${2}\" is not available" fi - FTLinstall "${binary}" "${path}" - else echo -e " ${INFO} Requested option \"${1}\" is not available" exit 1 diff --git a/pihole b/pihole index 1bd35dbb..8da911d8 100755 --- a/pihole +++ b/pihole @@ -429,15 +429,15 @@ Time: # Enable Pi-hole echo -e " ${INFO} Enabling blocking" local str="Pi-hole Enabled" - + sed -i 's/^#addn-hosts/addn-hosts/' /etc/dnsmasq.d/01-pihole.conf if [[ -e "/etc/pihole/wildcard.list" ]]; then mv "/etc/pihole/wildcard.list" "$wildcardlist" fi fi - + restartDNS - + echo -e "${OVER} ${TICK} ${str}" } @@ -535,6 +535,7 @@ Switch Pi-hole subsystems to a different Github branch Repositories: core [branch] Change the branch of Pi-hole's core subsystem web [branch] Change the branch of Admin Console subsystem + ftl [branch] Change the branch of Pi-hole's FTL subsystem Branches: master Update subsystems to the latest stable release From 247d3ed7299822cad1c3a71a9c999ffd6ff6e8dc Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Sun, 16 Jul 2017 13:54:23 +0100 Subject: [PATCH 18/28] list availible branches for FTL Signed-off-by: Adam Warner --- advanced/Scripts/piholeCheckout.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/advanced/Scripts/piholeCheckout.sh b/advanced/Scripts/piholeCheckout.sh index c6f55dd9..f214d2ce 100644 --- a/advanced/Scripts/piholeCheckout.sh +++ b/advanced/Scripts/piholeCheckout.sh @@ -331,6 +331,10 @@ checkout() { FTLinstall "${binary}" "${path}" else echo " ${CROSS} Requested branch \"${2}\" is not available" + ftlbranches=( $(git ls-remote https://github.com/pi-hole/ftl | grep 'heads' | sed 's/refs\/heads\///;s/ //g' | awk '{print $2}') ) + echo -e " ${INFO} Available branches for FTL are:" + for e in "${ftlbranches[@]}"; do echo " - $e"; done + exit 1 fi else From c9a98b68c8eff21e997c10f3282686ae820f833f Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Mon, 17 Jul 2017 01:44:14 +1000 Subject: [PATCH 19/28] Avoid reactivating a deactivated lighttpd service (#1485) * Do not activate disabled lighttpd upon update * Fixes #1362 * Use systemctl when available * Move `finalexports` to the very end of the install script set value of LIGHTTPD_ENABLED to 1 or 0 depending on whether or not lighttpd is enabled or disabled. actually save LIGHTTPD_ENABLED value to setupvars.conf Signed-off-by: Adam Warner * add [[ -z "${LIGHTTPD_ENABLED}" ]] back in! Signed-off-by: Adam Warner * Ensure "Loaded:" is the line being checked * Colourise disabled lighttpd message * Prevent disabled lighttpd triggering error * change of plan, don't need that [[ -z "${LIGHTTPD_ENABLED}" ]] Signed-off-by: Adam Warner --- automated install/basic-install.sh | 35 ++++++++++++++++++++---------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 74e2a61d..a3c41b11 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1485,8 +1485,7 @@ finalExports() { # If the setup variable file exists, if [ -e "${setupVars}" ]; then - # update the variables in the file - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;/INSTALL_WEB/d;' "${setupVars}" + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;/INSTALL_WEB/d;/LIGHTTPD_ENABLED/d;' "${setupVars}" fi # echo the information to the user { @@ -1497,6 +1496,7 @@ finalExports() { echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}" echo "QUERY_LOGGING=${QUERY_LOGGING}" echo "INSTALL_WEB=${INSTALL_WEB}" + echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" }>> "${setupVars}" # Look for DNS server settings which would have to be reapplied @@ -1585,9 +1585,6 @@ installPihole() { FTLdetect || echo -e " ${CROSS} FTL Engine not installed." # Configure the firewall configureFirewall - # Run the final exports - finalExports - #runGravity } # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. @@ -1621,8 +1618,8 @@ updatePihole() { installLogrotate # Detect if FTL is installed FTLdetect || echo -e " ${CROSS} FTL Engine not installed." - finalExports #re-export setupVars.conf to account for any new vars added in new versions - #runGravity + + } @@ -2052,10 +2049,24 @@ main() { enable_service dnsmasq # If the Web server was installed, - if [[ ${INSTALL_WEB} == true ]]; then - # enable it - start_service lighttpd - enable_service lighttpd + if [[ "${INSTALL_WEB}" == true ]]; then + # Check to see if lighttpd was already set to run on reboot + if [[ "${useUpdateVars}" == true ]]; then + if [[ -x "$(command -v systemctl)" ]]; then + # Value will either be 1, if true, or 0 + LIGHTTPD_ENABLED=$(systemctl is-enabled lighttpd | grep -c 'enabled' || true) + else + # Value will either be 1, if true, or 0 + LIGHTTPD_ENABLED=$(service lighttpd status | awk '/Loaded:/ {print $0}' | grep -c 'enabled' || true) + fi + fi + + if [[ "${LIGHTTPD_ENABLED}" == "1" ]]; then + start_service lighttpd + enable_service lighttpd + else + echo -e " ${INFO} Lighttpd is disabled, skipping service restart" + fi fi # Download and compile the aggregated block list @@ -2103,6 +2114,8 @@ main() { # Display where the log file is echo -e "\n ${INFO} The install log is located at: /etc/pihole/install.log ${COL_LIGHT_GREEN}${INSTALL_TYPE} Complete! ${COL_NC}" + #update setupvars.conf with any variables that may or may not have been changed during the install + finalExports } # From 70fb733fea71858494f441d429513d6a77674b21 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Mon, 17 Jul 2017 10:53:49 +0100 Subject: [PATCH 20/28] col_table does not exist yet, will break the install if pushed as hotfix. Not sure why these additional "'s were put in, they break the update command, too. Signed-off-by: Adam Warner --- advanced/Scripts/update.sh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index e3a7f8fd..2ef136a9 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -26,8 +26,6 @@ PH_TEST=true #shellcheck disable=SC1090 source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" -source "/opt/pihole/COL_TABLE" - # is_repo() sourced from basic-install.sh # make_repo() sourced from basic-install.sh # update_repo() source from basic-install.sh @@ -43,7 +41,7 @@ GitCheckUpdateAvail() { # @ alone is a shortcut for HEAD. Older versions of git # need @{0} - LOCAL="$("git rev-parse @{0}")" + LOCAL="$(git rev-parse @{0})" # The suffix @{upstream} to a branchname # (short form @{u}) refers @@ -52,7 +50,7 @@ GitCheckUpdateAvail() { # (configured with branch..remote and # branch..merge). A missing branchname # defaults to the current one. - REMOTE="$("git rev-parse @{upstream}")" + REMOTE="$(git rev-parse @{upstream})" if [[ ${#LOCAL} == 0 ]]; then echo "::: Error: Local revision could not be obtained, ask Pi-hole support." From 83592a5e70a627ea1ba5ab1fce6c99112e9e8f6b Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Mon, 17 Jul 2017 11:14:02 +0100 Subject: [PATCH 21/28] Put '"'s in the right place Signed-off-by: Adam Warner --- advanced/Scripts/update.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 2ef136a9..4281d69f 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -41,7 +41,7 @@ GitCheckUpdateAvail() { # @ alone is a shortcut for HEAD. Older versions of git # need @{0} - LOCAL="$(git rev-parse @{0})" + LOCAL="$(git rev-parse "@{0}")" # The suffix @{upstream} to a branchname # (short form @{u}) refers @@ -50,7 +50,7 @@ GitCheckUpdateAvail() { # (configured with branch..remote and # branch..merge). A missing branchname # defaults to the current one. - REMOTE="$(git rev-parse @{upstream})" + REMOTE="$(git rev-parse "@{upstream}")" if [[ ${#LOCAL} == 0 ]]; then echo "::: Error: Local revision could not be obtained, ask Pi-hole support." From 1c93868ae1b1bc673f26ae4c5a9d235897ca99aa Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Mon, 24 Jul 2017 23:22:04 +0100 Subject: [PATCH 22/28] Adjust wording of echos Signed-off-by: Adam Warner --- automated install/basic-install.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 3a53fca5..f0d6018f 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1882,6 +1882,7 @@ FTLdetect() { #In the next section we check to see if FTL is already installed (in case of pihole -r). #If the installed version matches the latest version, then check the installed sha1sum of the binary vs the remote sha1sum. If they do not match, then download + echo -e " ${INFO} Checking for existing FTL binary..." local FTLversion=$(/usr/bin/pihole-FTL tag) local FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n') @@ -1889,15 +1890,16 @@ FTLdetect() { # Install FTL FTLinstall "${binary}" || return 1 else + echo -e " ${INFO} Latest FTL Binary already installed (${FTLlatesttag}). Confirming Checksum..." + local remoteSha1=$(curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${FTLversion%$'\r'}/${binary}.sha1" | cut -d ' ' -f 1) local localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1) - echo -e " ${INFO} Existing FTL Binary detected. Checking sha1sum..." if [[ "${remoteSha1}" != "${localSha1}" ]]; then echo -e " ${INFO} Corruption detected..." FTLinstall "${binary}" || return 1 else - echo -e " ${INFO} sha1sums match. No need to download!" + echo -e " ${INFO} Checksum correct. No need to download!" fi fi } From a293b5a3718f3381f9f9fd2cc955127d062e1a99 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Tue, 25 Jul 2017 22:49:06 +0100 Subject: [PATCH 23/28] prevent `./automated install/basic-install.sh: line 1886: /usr/bin/pihole-FTL: No such file or directory` on new install, or if pihole-FTL is missing for whatever reason. Signed-off-by: Adam Warner --- automated install/basic-install.sh | 38 +++++++++++++++++++----------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index f0d6018f..170754f2 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1883,25 +1883,35 @@ FTLdetect() { #In the next section we check to see if FTL is already installed (in case of pihole -r). #If the installed version matches the latest version, then check the installed sha1sum of the binary vs the remote sha1sum. If they do not match, then download echo -e " ${INFO} Checking for existing FTL binary..." - local FTLversion=$(/usr/bin/pihole-FTL tag) - local FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n') - if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then - # Install FTL - FTLinstall "${binary}" || return 1 - else - echo -e " ${INFO} Latest FTL Binary already installed (${FTLlatesttag}). Confirming Checksum..." + local ftlLoc=$(which pihole-FTL) - local remoteSha1=$(curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${FTLversion%$'\r'}/${binary}.sha1" | cut -d ' ' -f 1) - local localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1) + if [[ ${ftlLoc} ]]; then + local FTLversion=$(/usr/bin/pihole-FTL tag) + local FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n') - if [[ "${remoteSha1}" != "${localSha1}" ]]; then - echo -e " ${INFO} Corruption detected..." - FTLinstall "${binary}" || return 1 + if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then + # Install FTL + FTLinstall "${binary}" || return 1 else - echo -e " ${INFO} Checksum correct. No need to download!" + echo -e " ${INFO} Latest FTL Binary already installed (${FTLlatesttag}). Confirming Checksum..." + + local remoteSha1=$(curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${FTLversion%$'\r'}/${binary}.sha1" | cut -d ' ' -f 1) + local localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1) + + if [[ "${remoteSha1}" != "${localSha1}" ]]; then + echo -e " ${INFO} Corruption detected..." + FTLinstall "${binary}" || return 1 + else + echo -e " ${INFO} Checksum correct. No need to download!" + fi fi - fi + else + # Install FTL + FTLinstall "${binary}" || return 1 + fi + + } main() { From 714fd93292880e999706419480f4c59a0defb77f Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Wed, 26 Jul 2017 08:03:23 +1000 Subject: [PATCH 24/28] Provide correct FTL stats (#1619) * Provide correct FTL stats * Use the correct lines in the array when displaying FTL stats * Treat ads_blocked_today/dns_queries_today as currency, like domains_being_blocked is on large screens * Chronometer Shellcheck validation * Remove unnecessary \ from CPU usage readout --- advanced/Scripts/chronometer.sh | 42 ++++++++++++++++++--------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/advanced/Scripts/chronometer.sh b/advanced/Scripts/chronometer.sh index d9b01fc0..a9ccf900 100755 --- a/advanced/Scripts/chronometer.sh +++ b/advanced/Scripts/chronometer.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash +# shellcheck disable=SC1090,SC1091 # Pi-hole: A black hole for Internet advertisements # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. @@ -91,10 +92,10 @@ printFunc() { printf "%s%s$spc" "$title" "$text_main" if [[ -n "$text_addn" ]]; then - printf "%s(%s)%s\n" "$COL_NC$COL_DARK_GRAY" "$text_addn" "$COL_NC" + printf "%s(%s)%s\\n" "$COL_NC$COL_DARK_GRAY" "$text_addn" "$COL_NC" else # Do not print trailing newline on final line - [[ -z "$text_last" ]] && printf "%s\n" "$COL_NC" + [[ -z "$text_last" ]] && printf "%s\\n" "$COL_NC" fi } @@ -126,7 +127,7 @@ get_init_stats() { mins=$(( ($1%3600)/60 )); secs=$(( $1%60 )) [[ "$day" -ge "2" ]] && plu="s" [[ "$day" -ge "1" ]] && days="$day day${plu}, " || days="" - printf "%s%02d:%02d:%02d\n" "$days" "$hrs" "$mins" "$secs" + printf "%s%02d:%02d:%02d\\n" "$days" "$hrs" "$mins" "$secs" } # Set Colour Codes @@ -285,6 +286,7 @@ get_sys_stats() { sys_loadavg=$(cut -d " " -f1,2,3 /proc/loadavg) # Get CPU usage, only counting processes over 1% as active + # shellcheck disable=SC2009 cpu_raw=$(ps -eo pcpu,rss --no-headers | grep -E -v " 0") cpu_tasks=$(wc -l <<< "$cpu_raw") cpu_taskact=$(sed -r "/(^ 0.)/d" <<< "$cpu_raw" | wc -l) @@ -306,7 +308,7 @@ get_sys_stats() { # Determine colour for temperature if [[ -n "$temp_file" ]]; then if [[ "$temp_unit" == "C" ]]; then - cpu_temp=$(printf "%.0fc\n" "$(calcFunc "$(< $temp_file) / 1000")") + cpu_temp=$(printf "%.0fc\\n" "$(calcFunc "$(< $temp_file) / 1000")") case "${cpu_temp::-1}" in -*|[0-9]|[1-3][0-9]) cpu_col="$COL_LIGHT_BLUE";; @@ -320,7 +322,7 @@ get_sys_stats() { cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" elif [[ "$temp_unit" == "F" ]]; then - cpu_temp=$(printf "%.0ff\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")") + cpu_temp=$(printf "%.0ff\\n" "$(calcFunc "($(< $temp_file) / 1000) * 9 / 5 + 32")") case "${cpu_temp::-1}" in -*|[0-9]|[0-9][0-9]) cpu_col="$COL_LIGHT_BLUE";; @@ -333,7 +335,7 @@ get_sys_stats() { cpu_temp_str=" @ $cpu_col$cpu_temp$COL_NC$COL_DARK_GRAY" else - cpu_temp_str=$(printf " @ %.0fk\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")") + cpu_temp_str=$(printf " @ %.0fk\\n" "$(calcFunc "($(< $temp_file) / 1000) + 273.15")") fi else cpu_temp_str="" @@ -365,12 +367,12 @@ get_ftl_stats() { local stats_raw mapfile -t stats_raw < <(pihole-FTL "stats") - domains_being_blocked_raw="${stats_raw[1]#* }" - dns_queries_today_raw="${stats_raw[3]#* }" - ads_blocked_today_raw="${stats_raw[5]#* }" - ads_percentage_today_raw="${stats_raw[7]#* }" - queries_forwarded_raw="${stats_raw[11]#* }" - queries_cached_raw="${stats_raw[13]#* }" + domains_being_blocked_raw="${stats_raw[0]#* }" + dns_queries_today_raw="${stats_raw[1]#* }" + ads_blocked_today_raw="${stats_raw[2]#* }" + ads_percentage_today_raw="${stats_raw[3]#* }" + queries_forwarded_raw="${stats_raw[5]#* }" + queries_cached_raw="${stats_raw[6]#* }" # Only retrieve these stats when not called from jsonFunc if [[ -z "$1" ]]; then @@ -378,11 +380,11 @@ get_ftl_stats() { local top_domain_raw local top_client_raw - domains_being_blocked=$(printf "%.0f\n" "${domains_being_blocked_raw}") - dns_queries_today=$(printf "%.0f\n" "${dns_queries_today_raw}") - ads_blocked_today=$(printf "%.0f\n" "${ads_blocked_today_raw}") - ads_percentage_today=$(printf "%'.0f\n" "${ads_percentage_today_raw}") - queries_cached_percentage=$(printf "%.0f\n" "$(calcFunc "$queries_cached_raw * 100 / ( $queries_forwarded_raw + $queries_cached_raw )")") + domains_being_blocked=$(printf "%.0f\\n" "${domains_being_blocked_raw}") + dns_queries_today=$(printf "%.0f\\n" "${dns_queries_today_raw}") + ads_blocked_today=$(printf "%.0f\\n" "${ads_blocked_today_raw}") + ads_percentage_today=$(printf "%'.0f\\n" "${ads_percentage_today_raw}") + queries_cached_percentage=$(printf "%.0f\\n" "$(calcFunc "$queries_cached_raw * 100 / ( $queries_forwarded_raw + $queries_cached_raw )")") recent_blocked=$(pihole-FTL recentBlocked) read -r -a top_ad_raw <<< "$(pihole-FTL "top-ads (1)")" read -r -a top_domain_raw <<< "$(pihole-FTL "top-domains (1)")" @@ -412,6 +414,8 @@ get_strings() { used_str="Used: " leased_str="Leased: " domains_being_blocked=$(printf "%'.0f" "$domains_being_blocked") + ads_blocked_today=$(printf "%'.0f" "$ads_blocked_today") + dns_queries_today=$(printf "%'.0f" "$dns_queries_today") ph_info="Blocking: $domains_being_blocked sites" total_str="Total: " else @@ -473,8 +477,8 @@ chronoFunc() { ${COL_DARK_GRAY}$scr_line_str${COL_NC}" else echo -e "|Β―Β―Β―(Β―)_|Β―|_ ___|Β―|___$phc_ver_str -| Β―_/Β―|_| ' \/ _ \ / -_)$lte_ver_str -|_| |_| |_||_\___/_\___|$ftl_ver_str +| Β―_/Β―|_| ' \\/ _ \\ / -_)$lte_ver_str +|_| |_| |_||_\\___/_\\___|$ftl_ver_str ${COL_DARK_GRAY}$scr_line_str${COL_NC}" fi From e0eb5eb2b1bb5fc5772aac479f71d4dfb77237ac Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Wed, 26 Jul 2017 08:07:06 +1000 Subject: [PATCH 25/28] Fix queryFunc if adlists URLs have been removed (#1618) * Fix queryFunc if adlists URLs have been removed * Allow for -adlists command line switch (where the "s" is a typo) * Add error message when unable to find associated adlists URL * Provide PR fix on current dev version * Add blResult variable for white/black match * Supporting Block Page queryFunc fixes * Re-add unmerged lines --- pihole | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/pihole b/pihole index 8da911d8..61ed6cd6 100755 --- a/pihole +++ b/pihole @@ -149,7 +149,7 @@ Options: fi # Strip valid options, leaving only the domain and invalid options - options=$(sed 's/ \?-\(exact\|adlist\|bp\|all\) \?//g' <<< "$options") + options=$(sed 's/ \?-\(exact\|adlist\(s\)\?\|bp\|all\) \?//g' <<< "$options") # Handle errors if [[ "${options}" == *" "* ]]; then @@ -181,13 +181,14 @@ Options: results=($(scanList "${query}" "${lists}" "${method}")) if [[ -n "${results[*]}" ]]; then + blResult=true # Loop through each scanList line to print appropriate title for result in "${results[@]}"; do filename="${result/:*/}" if [[ -n "$exact" ]]; then printf " Exact result in %s\n" "${filename}" elif [[ -n "$blockpage" ]]; then - printf " [i] %s\n" "${filename}" + printf "Ο€ %s\n" "${filename}" else domain="${result/*:/}" if [[ ! "${filename}" == "${filename_prev:-}" ]]; then @@ -216,7 +217,7 @@ Options: fi if [[ -n "${blockpage}" ]]; then - echo " ${INFO} ${match}" + echo "Ο€ ${wildcardlist/*\/}" else echo " *.${match}" fi @@ -287,12 +288,18 @@ Options: filenum=("${filename/list./}") filenum=("${filenum/.*/}") filename="${adlists[$filenum]}" + + # If gravity has generated associated .domains files + # but adlists.list has been modified since + if [[ -z "${filename}" ]]; then + filename="${COL_LIGHT_RED}Error: no associated adlists URL found${COL_NC}" + fi fi if [[ -n "${exact}" ]]; then printf " %s\n" "${filename}" elif [[ -n "${blockpage}" ]]; then - printf " [%s] %s\n" "${filenum}" "${filename}" + printf "%s %s\n" "${filenum}" "${filename}" else # Standard query output # Print filename heading once per file, not for every match From c02a24cf7176f73c8ec8d1abf28a807e69611c17 Mon Sep 17 00:00:00 2001 From: Adam Warner Date: Wed, 26 Jul 2017 14:34:40 +0100 Subject: [PATCH 26/28] Fix/unbreak development (#1635) * Always process DNS and DHCP settings in installer * change where finalExports is called and where LIGHTTPD_ENABLED is set. Signed-off-by: Adam Warner * this may or may not work. If it does, can be functionised to reduce code duping Signed-off-by: Adam Warner * This will fix the tests, but break the patch Signed-off-by: Adam Warner --- automated install/basic-install.sh | 43 +++++++++++++++++------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 4330637d..e37282a7 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1499,22 +1499,20 @@ finalExports() { echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}" }>> "${setupVars}" - # Look for DNS server settings which would have to be reapplied + # Bring in the current settings and the functions to manipulate them source "${setupVars}" - # source "${PI_HOLE_LOCAL_REPO}/advanced/Scripts/webpage.sh" - # if [[ "${DNS_FQDN_REQUIRED}" != "" ]] ; then - # + # Look for DNS server settings which would have to be reapplied ProcessDNSSettings fi - # if [[ "${DHCP_ACTIVE}" != "" ]] ; then - # + # Look for DHCP server settings which would have to be reapplied ProcessDHCPSettings fi + } # Install the logrotate script @@ -1585,6 +1583,9 @@ installPihole() { FTLdetect || echo -e " ${CROSS} FTL Engine not installed." # Configure the firewall configureFirewall + + #update setupvars.conf with any variables that may or may not have been changed during the install + finalExports } # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break. @@ -1619,6 +1620,8 @@ updatePihole() { # Detect if FTL is installed FTLdetect || echo -e " ${CROSS} FTL Engine not installed." + #update setupvars.conf with any variables that may or may not have been changed during the install + finalExports } @@ -2034,6 +2037,13 @@ main() { fi install_dependent_packages DEPS[@] + if [[ -x "$(command -v systemctl)" ]]; then + # Value will either be 1, if true, or 0 + LIGHTTPD_ENABLED=$(systemctl is-enabled lighttpd | grep -c 'enabled' || true) + else + # Value will either be 1, if true, or 0 + LIGHTTPD_ENABLED=$(service lighttpd status | awk '/Loaded:/ {print $0}' | grep -c 'enabled' || true) + fi # Install and log everything to a file installPihole | tee ${tmpLog} @@ -2055,6 +2065,14 @@ main() { fi install_dependent_packages DEPS[@] + if [[ -x "$(command -v systemctl)" ]]; then + # Value will either be 1, if true, or 0 + LIGHTTPD_ENABLED=$(systemctl is-enabled lighttpd | grep -c 'enabled' || true) + else + # Value will either be 1, if true, or 0 + LIGHTTPD_ENABLED=$(service lighttpd status | awk '/Loaded:/ {print $0}' | grep -c 'enabled' || true) + fi + updatePihole | tee ${tmpLog} fi @@ -2080,16 +2098,6 @@ main() { # If the Web server was installed, if [[ "${INSTALL_WEB}" == true ]]; then - # Check to see if lighttpd was already set to run on reboot - if [[ "${useUpdateVars}" == true ]]; then - if [[ -x "$(command -v systemctl)" ]]; then - # Value will either be 1, if true, or 0 - LIGHTTPD_ENABLED=$(systemctl is-enabled lighttpd | grep -c 'enabled' || true) - else - # Value will either be 1, if true, or 0 - LIGHTTPD_ENABLED=$(service lighttpd status | awk '/Loaded:/ {print $0}' | grep -c 'enabled' || true) - fi - fi if [[ "${LIGHTTPD_ENABLED}" == "1" ]]; then start_service lighttpd @@ -2144,8 +2152,7 @@ main() { # Display where the log file is echo -e "\n ${INFO} The install log is located at: /etc/pihole/install.log ${COL_LIGHT_GREEN}${INSTALL_TYPE} Complete! ${COL_NC}" - #update setupvars.conf with any variables that may or may not have been changed during the install - finalExports + } # From b09c660833b322eec21a1f84e1044557c274478a Mon Sep 17 00:00:00 2001 From: Mcat12 Date: Wed, 26 Jul 2017 12:15:23 -0400 Subject: [PATCH 27/28] Always process DNS and DHCP settings in installer (#1630) * Always process DNS and DHCP settings in installer * Make sure dnsmasq config exists before modifying it Signed-off-by: Mcat12 * Make sure the dnsmasq config directory exists Signed-off-by: Mcat12 * Only remove the DHCP config if it exists (fixes tests, hopefully) Signed-off-by: Mcat12 --- advanced/Scripts/webpage.sh | 4 +++- automated install/basic-install.sh | 11 +++-------- test/test_automated_install.py | 2 ++ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index 42272122..b887a2b8 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -292,7 +292,9 @@ ra-param=*,0,0 fi else - rm "${dhcpconfig}" &> /dev/null + if [[ -f "${dhcpconfig}" ]]; then + rm "${dhcpconfig}" &> /dev/null + fi fi } diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index e37282a7..9d264846 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1503,16 +1503,11 @@ finalExports() { source "${setupVars}" source "${PI_HOLE_LOCAL_REPO}/advanced/Scripts/webpage.sh" - if [[ "${DNS_FQDN_REQUIRED}" != "" ]] ; then # Look for DNS server settings which would have to be reapplied - ProcessDNSSettings - fi - - if [[ "${DHCP_ACTIVE}" != "" ]] ; then - # Look for DHCP server settings which would have to be reapplied - ProcessDHCPSettings - fi + ProcessDNSSettings + # Look for DHCP server settings which would have to be reapplied + ProcessDHCPSettings } # Install the logrotate script diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 4a4f72aa..9129c314 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -59,6 +59,8 @@ def test_setupVars_saved_to_file(Pihole): TERM=xterm source /opt/pihole/basic-install.sh {} + mkdir -p /etc/dnsmasq.d + version_check_dnsmasq finalExports cat /etc/pihole/setupVars.conf '''.format(set_setup_vars)) From fdf2649f2fef8981176735fb856997e4856fc13b Mon Sep 17 00:00:00 2001 From: WaLLy3K Date: Thu, 27 Jul 2017 03:00:08 +1000 Subject: [PATCH 28/28] Clean up known remaining colour output issues (#1613) * Print newline on error message * Output last three lines of error if update fails * Consistent error messages & housekeeping * Add shellcheck directive to ignore COL_TABLE * Quoted and braced variables for codebase consistency * Escaped newlines correctly * Made error messages consistent (indenting and wording) * Removed consecutive echos * Conditional formatting consistency * Braced, quoted and used [[ on conditionals * Fix specific ShellCheck issues * Fixed issues that could be safely changed without extensive testing * Update SELinux whiptail behaviour & more * Colourised some strings * Fixed multiple line string indenting * Made output consistent with existing codebase * Removed sequential echos * Make SELinux whiptail use "--defaultno", and change text wording * Add help text for hostrecord, and colourise output * this should fix the tests... Signed-off-by: Adam Warner * revert changes to `update_package_cache()` to prove tests Signed-off-by: Adam Warner --- advanced/Scripts/update.sh | 76 ++++---- advanced/Scripts/webpage.sh | 20 +- automated install/basic-install.sh | 283 ++++++++++++++--------------- test/test_automated_install.py | 6 +- 4 files changed, 194 insertions(+), 191 deletions(-) diff --git a/advanced/Scripts/update.sh b/advanced/Scripts/update.sh index 71b7cecd..a4ada4c8 100755 --- a/advanced/Scripts/update.sh +++ b/advanced/Scripts/update.sh @@ -19,10 +19,9 @@ readonly PI_HOLE_FILES_DIR="/etc/.pihole" # shellcheck disable=SC2034 PH_TEST=true -# Have to ignore the following rule as spaces in paths are not supported by ShellCheck -#shellcheck disable=SC1090 +# shellcheck disable=SC1090 source "${PI_HOLE_FILES_DIR}/automated install/basic-install.sh" - +# shellcheck disable=SC1091 source "/opt/pihole/COL_TABLE" # is_repo() sourced from basic-install.sh @@ -51,15 +50,15 @@ GitCheckUpdateAvail() { # defaults to the current one. REMOTE="$(git rev-parse "@{upstream}")" - if [[ ${#LOCAL} == 0 ]]; then - echo -e " ${COL_LIGHT_RED}Error: Local revision could not be obtained, ask Pi-hole support." - echo -e " Additional debugging output:${COL_NC}" + if [[ "${#LOCAL}" == 0 ]]; then + echo -e "\\n ${COL_LIGHT_RED}Error: Local revision could not be obtained, please contact Pi-hole Support + Additional debugging output:${COL_NC}" git status exit fi - if [[ ${#REMOTE} == 0 ]]; then - echo -e " ${COL_LIGHT_RED}Error: Remote revision could not be obtained, ask Pi-hole support." - echo -e " Additional debugging output:${COL_NC}" + if [[ "${#REMOTE}" == 0 ]]; then + echo -e "\\n ${COL_LIGHT_RED}Error: Remote revision could not be obtained, please contact Pi-hole Support + Additional debugging output:${COL_NC}" git status exit fi @@ -94,13 +93,15 @@ FTLcheckUpdate() { main() { local pihole_version_current local web_version_current - #shellcheck disable=1090,2154 + local basicError="\\n ${COL_LIGHT_RED}Unable to complete update, please contact Pi-hole Support${COL_NC}" + + # shellcheck disable=1090,2154 source "${setupVars}" - #This is unlikely + # This is unlikely if ! is_repo "${PI_HOLE_FILES_DIR}" ; then - echo -e " ${COL_LIGHT_RED}Critical 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 "\\n ${COL_LIGHT_RED}Error: Core Pi-hole repo is missing from system! + Please re-run install script from https://pi-hole.net${COL_NC}" exit 1; fi @@ -108,18 +109,18 @@ main() { if GitCheckUpdateAvail "${PI_HOLE_FILES_DIR}" ; then core_update=true - 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 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_LIGHT_GREEN}up to date${COL_NC}" fi if FTLcheckUpdate ; then FTL_update=true - echo -e " ${INFO} FTL:\t\t${COL_YELLOW}update available${COL_NC}" + echo -e " ${INFO} FTL:\\t\\t${COL_YELLOW}update available${COL_NC}" else FTL_update=false - echo -e " ${INFO} FTL:\t\t${COL_LIGHT_GREEN}up to date${COL_NC}" + echo -e " ${INFO} FTL:\\t\\t${COL_LIGHT_GREEN}up to date${COL_NC}" fi # Logic: Don't update FTL when there is a core update available @@ -132,19 +133,19 @@ main() { echo "" fi - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then if ! is_repo "${ADMIN_INTERFACE_DIR}" ; then - echo -e " ${COL_LIGHT_RED}Critical 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 "\\n ${COL_LIGHT_RED}Error: Web Admin repo is missing from system! + Please re-run install script from https://pi-hole.net${COL_NC}" exit 1; fi if GitCheckUpdateAvail "${ADMIN_INTERFACE_DIR}" ; then web_update=true - 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 web_update=false - echo -e " ${INFO} Web Interface:\t${COL_LIGHT_GREEN}up to date${COL_NC}" + echo -e " ${INFO} Web Interface:\\t${COL_LIGHT_GREEN}up to date${COL_NC}" fi # Logic @@ -163,25 +164,24 @@ main() { echo -e " ${TICK} Everything is up to date!" exit 0 fi - elif ! ${core_update} && ${web_update} ; then echo "" echo -e " ${INFO} Pi-hole Web Admin files out of date" getGitFiles "${ADMIN_INTERFACE_DIR}" "${ADMIN_INTERFACE_GIT_URL}" - elif ${core_update} && ! ${web_update} ; then echo "" echo -e " ${INFO} Pi-hole core files out of date" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" - ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo -e " ${COL_LIGHT_RED}Unable to complete update, contact Pi-hole${COL_NC}" && exit 1 - + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \ + echo -e "${basicError}" && exit 1 elif ${core_update} && ${web_update} ; then echo "" echo -e " ${INFO} Updating Pi-hole core and web admin files" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" - ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --unattended || echo -e " ${COL_LIGHT_RED}Unable to complete update, contact Pi-hole${COL_NC}" && exit 1 + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --unattended || \ + echo -e "${basicError}" && exit 1 else - echo -e " ${COL_LIGHT_RED}Update script has malfunctioned, fallthrough reached. Please contact support${COL_NC}" + echo -e " ${COL_LIGHT_RED}Update script has malfunctioned, please contact Pi-hole Support${COL_NC}" exit 1 fi else # Web Admin not installed, so only verify if core is up to date @@ -193,38 +193,36 @@ main() { fi else echo "" - echo -e " ${INFO} Pi-hole core files out of date" + echo -e " ${INFO} Pi-hole Core files out of date" getGitFiles "${PI_HOLE_FILES_DIR}" "${PI_HOLE_GIT_URL}" - ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || echo -e " ${COL_LIGHT_RED}Unable to complete update, contact Pi-hole${COL_NC}" && exit 1 + ${PI_HOLE_FILES_DIR}/automated\ install/basic-install.sh --reconfigure --unattended || \ + echo -e "${basicError}" && exit 1 fi fi if [[ "${web_update}" == true ]]; then web_version_current="$(/usr/local/bin/pihole version --admin --current)" echo "" - echo -e " ${INFO} Web Admin version is now at ${web_version_current/* v/v}" - echo -e " ${INFO} If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'" + echo -e " ${INFO} Web Admin version is now at ${web_version_current/* v/v} + ${INFO} If you had made any changes in '/var/www/html/admin/', they have been stashed using 'git stash'" fi if [[ "${core_update}" == true ]]; then pihole_version_current="$(/usr/local/bin/pihole version --pihole --current)" echo "" - echo -e " ${INFO} Pi-hole version is now at ${pihole_version_current/* v/v}" - echo -e " ${INFO} If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'" + echo -e " ${INFO} Pi-hole version is now at ${pihole_version_current/* v/v} + ${INFO} If you had made any changes in '/etc/.pihole/', they have been stashed using 'git stash'" fi - if [[ ${FTL_update} == true ]]; then + if [[ "${FTL_update}" == true ]]; then FTL_version_current="$(/usr/bin/pihole-FTL tag)" - echo "" - echo -e " ${INFO} FTL version is now at ${FTL_version_current/* v/v}" + echo -e "\\n ${INFO} FTL version is now at ${FTL_version_current/* v/v}" start_service pihole-FTL enable_service pihole-FTL fi - echo "" exit 0 - } main diff --git a/advanced/Scripts/webpage.sh b/advanced/Scripts/webpage.sh index b887a2b8..9d101482 100755 --- a/advanced/Scripts/webpage.sh +++ b/advanced/Scripts/webpage.sh @@ -29,6 +29,7 @@ Options: -c, celsius Set Celsius as preferred temperature unit -f, fahrenheit Set Fahrenheit as preferred temperature unit -k, kelvin Set Kelvin as preferred temperature unit + -r, hostrecord Add a name to the DNS associated to an IPv4/IPv6 address -h, --help Show this help dialog -i, interface Specify dnsmasq's interface listening behavior Add '-h' for more info on interface usage" @@ -392,12 +393,23 @@ RemoveDHCPStaticAddress() { } SetHostRecord() { - if [ -n "${args[3]}" ]; then + if [[ "${1}" == "-h" ]] || [[ "${1}" == "--help" ]]; then + echo "Usage: pihole -a hostrecord [IPv4-address],[IPv6-address] +Example: 'pihole -a hostrecord home.domain.com 192.168.1.1,2001:db8:a0b:12f0::1' +Add a name to the DNS associated to an IPv4/IPv6 address + +Options: + \"\" Empty: Remove host record + -h, --help Show this help dialog" + exit 0 + fi + + if [[ -n "${args[3]}" ]]; then change_setting "HOSTRECORD" "${args[2]},${args[3]}" - echo "Setting host record for ${args[2]} -> ${args[3]}" + echo -e " ${TICK} Setting host record for ${args[2]} to ${args[3]}" else change_setting "HOSTRECORD" "" - echo "Removing host record" + echo -e " ${TICK} Removing host record" fi ProcessDNSSettings @@ -474,7 +486,7 @@ main() { "resolve" ) ResolutionSettings;; "addstaticdhcp" ) AddDHCPStaticAddress;; "removestaticdhcp" ) RemoveDHCPStaticAddress;; - "hostrecord" ) SetHostRecord;; + "-r" | "hostrecord" ) SetHostRecord "$3";; "-i" | "interface" ) SetListeningMode "$@";; "-t" | "teleporter" ) Teleporter;; "adlist" ) CustomizeAdLists;; diff --git a/automated install/basic-install.sh b/automated install/basic-install.sh index 9d264846..4047502b 100755 --- a/automated install/basic-install.sh +++ b/automated install/basic-install.sh @@ -1,22 +1,21 @@ #!/usr/bin/env bash +# shellcheck disable=SC1090 + # Pi-hole: A black hole for Internet advertisements # (c) 2017 Pi-hole, LLC (https://pi-hole.net) # Network-wide ad blocking via your own hardware. # -# Installs Pi-hole +# Installs and Updates Pi-hole # # This file is copyright under the latest version of the EUPL. # Please see LICENSE file for your rights under this license. - - # pi-hole.net/donate # # Install with this command (from your Linux machine): # # curl -L install.pi-hole.net | bash - # -e option instructs bash to immediately exit if any command [1] has a non-zero exit status # We do not want users to end up with a partially working install, so we exit the script # instead of continuing the installation with something broken @@ -35,6 +34,7 @@ instalLogLoc=/etc/pihole/install.log # This is an important file as it contains information specific to the machine it's being installed on setupVars=/etc/pihole/setupVars.conf # Pi-hole uses lighttpd as a Web server, and this is the config file for it +# shellcheck disable=SC2034 lighttpdConfig=/etc/lighttpd/lighttpd.conf # This is a file used for the colorized output coltable=/opt/pihole/COL_TABLE @@ -79,7 +79,7 @@ reconfigure=false runUnattended=false # If the color table file exists, -if [[ -f ${coltable} ]]; then +if [[ -f "${coltable}" ]]; then # source it source ${coltable} # Othwerise, @@ -91,8 +91,9 @@ else TICK="[${COL_LIGHT_GREEN}βœ“${COL_NC}]" CROSS="[${COL_LIGHT_RED}βœ—${COL_NC}]" INFO="[i]" + # shellcheck disable=SC2034 DONE="${COL_LIGHT_GREEN} done!${COL_NC}" - OVER="\r\033[K" + OVER="\\r\\033[K" fi # A simple function that just echoes out our logo in ASCII format @@ -122,7 +123,6 @@ show_ascii_berry() { " } - # Compatibility distro_check() { # If apt-get is installed, then we know it's part of the Debian family @@ -321,13 +321,13 @@ getGitFiles() { # Show that we're checking it echo -e "${OVER} ${TICK} ${str}" # Update the repo, returning an error message on failure - update_repo "${directory}" || { echo -e "\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; } + update_repo "${directory}" || { echo -e "\\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; } # If it's not a .git repo, else # Show an error echo -e "${OVER} ${CROSS} ${str}" # Attempt to make the repository, showing an error on falure - make_repo "${directory}" "${remoteRepo}" || { echo -e "\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; } + make_repo "${directory}" "${remoteRepo}" || { echo -e "\\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; } fi # echo a blank line echo "" @@ -380,13 +380,13 @@ get_available_interfaces() { # A function for displaying the dialogs the user sees when first running the installer welcomeDialogs() { # Display the welcome dialog using an approriately sized window via the calculation conducted earlier in the script - whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\n\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} + whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\\n\\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c} # Request that users donate if they enjoy the software since we all work on it in our free time - whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\n\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} + whiptail --msgbox --backtitle "Plea" --title "Free and open source" "\\n\\nThe Pi-hole is free, but powered by your donations: http://pi-hole.net/donate" ${r} ${c} # Explain the need for a static address - whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\n\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. + whiptail --msgbox --backtitle "Initiating network interface" --title "Static IP Needed" "\\n\\nThe Pi-hole is a SERVER so it needs a STATIC IP ADDRESS to function properly. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c} } @@ -400,7 +400,8 @@ verifyFreeDiskSpace() { # Reqired space in KB local required_free_kilobytes=51200 # Calculate existing free space on this machine - local existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') + local existing_free_kilobytes + existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}') # If the existing space is not an integer, if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then @@ -414,7 +415,7 @@ verifyFreeDiskSpace() { # exit with an error code exit 1 # If there is insufficient free disk space, - elif [[ ${existing_free_kilobytes} -lt ${required_free_kilobytes} ]]; then + elif [[ "${existing_free_kilobytes}" -lt "${required_free_kilobytes}" ]]; then # show an error message echo -e " ${CROSS} ${str} Your system disk appears to only have ${existing_free_kilobytes} KB free @@ -428,7 +429,7 @@ verifyFreeDiskSpace() { e.g: curl -L https://install.pi-hole.net | bash" fi # Show there is not enough free space - echo -e "\n ${COL_LIGHT_RED}Insufficient free space, exiting...${COL_NC}" + echo -e "\\n ${COL_LIGHT_RED}Insufficient free space, exiting...${COL_NC}" # and exit with an error exit 1 # Otherwise, @@ -455,7 +456,7 @@ chooseInterface() { interfaceCount=$(echo "${availableInterfaces}" | wc -l) # If there is one interface, - if [[ ${interfaceCount} -eq 1 ]]; then + if [[ "${interfaceCount}" -eq 1 ]]; then # Set it as the interface to use since there is no other option PIHOLE_INTERFACE="${availableInterfaces}" # Otherwise, @@ -465,7 +466,7 @@ chooseInterface() { # use a variable to set the option as OFF to begin with mode="OFF" # If it's the first loop, - if [[ ${firstLoop} -eq 1 ]]; then + if [[ "${firstLoop}" -eq 1 ]]; then # set this as the interface to use (ON) firstLoop=0 mode="ON" @@ -501,11 +502,11 @@ testIPv6() { # will contain 162 which is the decimal value corresponding to 0xa2 value2=$(((0x$first)%256)) # the ULA test is testing for fc00::/7 according to RFC 4193 - ((($value1&254)==252)) && echo "ULA" || true + (((value1&254)==252)) && echo "ULA" || true # the GUA test is testing for 2000::/3 according to RFC 4291 - ((($value1&112)==32)) && echo "GUA" || true + (((value1&112)==32)) && echo "GUA" || true # the LL test is testing for fe80::/10 according to RFC 4193 - ((($value1==254) && (($value2&192)==128))) && echo "Link-local" || true + (((value1==254) && ((value2&192)==128))) && echo "Link-local" || true } # A dialog for showing the user about IPv6 blocking @@ -529,17 +530,17 @@ useIPv6dialog() { # set the IPv6 address to the ULA address IPV6_ADDRESS="${ULA_ADDRESS}" # Show this info to the user - echo "::: Found IPv6 ULA address, using it for blocking IPv6 ads" + echo -e " ${INFO} Found IPv6 ULA address, using it for blocking IPv6 ads" # Otherwise, if the GUA_ADDRESS has a value, elif [[ ! -z "${GUA_ADDRESS}" ]]; then # Let the user know - echo "::: Found IPv6 GUA address, using it for blocking IPv6 ads" + echo -e " ${INFO} Found IPv6 GUA address, using it for blocking IPv6 ads" # And assign it to the global variable IPV6_ADDRESS="${GUA_ADDRESS}" # If none of those work, else # explain that IPv6 blocking will not be used - echo "::: Found neither IPv6 ULA nor GUA address, blocking IPv6 ads will not be enabled" + echo -e " ${INFO} Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled" # So set the variable to be empty IPV6_ADDRESS="" fi @@ -575,14 +576,14 @@ use4andor6() { esac done # If IPv4 is to be used, - if [[ ${useIPv4} ]]; then + if [[ "${useIPv4}" ]]; then # Run our function to get the information we need find_IPv4_information getStaticIPv4Settings setStaticIPv4 fi # If IPv6 is to be used, - if [[ ${useIPv6} ]]; then + if [[ "${useIPv6}" ]]; then # Run our function to get this information useIPv6dialog fi @@ -590,7 +591,7 @@ use4andor6() { echo -e " ${INFO} IPv4 address: ${IPV4_ADDRESS}" echo -e " ${INFO} IPv6 address: ${IPV6_ADDRESS}" # If neither protocol is selected, - if [ ! ${useIPv4} ] && [ ! ${useIPv6} ]; then + if [[ ! "${useIPv4}" ]] && [[ ! "${useIPv6}" ]]; then # Show an error in red echo -e " ${COL_LIGHT_RED}Error: Neither IPv4 or IPv6 selected${COL_NC}" # and exit with an error @@ -616,7 +617,7 @@ It is also possible to use a DHCP reservation, but if you are going to do that, # Otherwise, we need to ask the user to input their desired settings. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP) # Start a loop to let the user enter their information with the chance to go back and edit it if necessary - until [[ ${ipSettingsCorrect} = True ]]; do + until [[ "${ipSettingsCorrect}" = True ]]; do # Ask for the IPv4 address IPV4_ADDRESS=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 address" --inputbox "Enter your desired IPv4 address" ${r} ${c} "${IPV4_ADDRESS}" 3>&1 1>&2 2>&3) || \ @@ -660,7 +661,7 @@ setStaticIPv4() { local IPADDR local CIDR # For the Debian family, if dhcpcd.conf exists, - if [[ -f /etc/dhcpcd.conf ]]; then + if [[ -f "/etc/dhcpcd.conf" ]]; then # check if the IP is already in the file if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then echo -e " ${INFO} Static IP already configured" @@ -672,10 +673,10 @@ setStaticIPv4() { ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}" # Also give a warning that the user may need to reboot their system echo -e " ${TICK} Set IP address to ${IPV4_ADDRESS%/*} - You may need to restart after the install is complete" + You may need to restart after the install is complete" fi # If it's not Debian, check if it's the Fedora family by checking for the file below - elif [[ -f /etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} ]];then + elif [[ -f "/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}" ]];then # If it exists, IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE} # check if the desired IP is already set @@ -710,7 +711,7 @@ setStaticIPv4() { fi # Show a warning that the user may need to restart echo -e " ${TICK} Set IP address to ${IPV4_ADDRESS%/*} - You may need to restart after the install is complete" + You may need to restart after the install is complete" fi # If all that fails, else @@ -727,7 +728,7 @@ valid_ip() { local stat=1 # If the IP matches the format xxx.xxx.xxx.xxx, - if [[ ${ip} =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + if [[ "${ip}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then # Save the old Interfal Field Separator in a variable OIFS=$IFS # and set the new one to a dot (period) @@ -737,8 +738,8 @@ valid_ip() { # Restore the IFS to what it was IFS=${OIFS} ## Evaluate each octet by checking if it's less than or equal to 255 (the max for each octet) - [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ - && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] + [[ "${ip[0]}" -le 255 && "${ip[1]}" -le 255 \ + && "${ip[2]}" -le 255 && "${ip[3]}" -le 255 ]] # Save the exit code stat=$? fi @@ -801,13 +802,13 @@ setDNS() { ;; Custom) # Until the DNS settings are selected, - until [[ ${DNSSettingsCorrect} = True ]]; do + until [[ "${DNSSettingsCorrect}" = True ]]; do # strInvalid="Invalid" # If the first - if [ ! ${PIHOLE_DNS_1} ]; then + if [[ ! "${PIHOLE_DNS_1}" ]]; then # and second upstream servers do not exist - if [ ! ${PIHOLE_DNS_2} ]; then + if [[ ! "${PIHOLE_DNS_2}" ]]; then # prePopulate="" # Otherwise, @@ -816,39 +817,39 @@ setDNS() { prePopulate=", ${PIHOLE_DNS_2}" fi # - elif [ ${PIHOLE_DNS_1} ] && [ ! ${PIHOLE_DNS_2} ]; then + elif [[ "${PIHOLE_DNS_1}" ]] && [[ ! "${PIHOLE_DNS_2}" ]]; then # prePopulate="${PIHOLE_DNS_1}" # - elif [ ${PIHOLE_DNS_1} ] && [ ${PIHOLE_DNS_2} ]; then + elif [[ "${PIHOLE_DNS_1}" ]] && [[ "${PIHOLE_DNS_2}" ]]; then # prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}" fi # Dialog for the user to enter custom upstream servers - piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\n\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) || \ + piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), seperated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) || \ { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } # PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}') PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}') # If the IP is valid, - if ! valid_ip "${PIHOLE_DNS_1}" || [ ! "${PIHOLE_DNS_1}" ]; then + if ! valid_ip "${PIHOLE_DNS_1}" || [[ ! "${PIHOLE_DNS_1}" ]]; then # store it in the variable so we can use it PIHOLE_DNS_1=${strInvalid} fi # Do the same for the secondary server - if ! valid_ip "${PIHOLE_DNS_2}" && [ "${PIHOLE_DNS_2}" ]; then + if ! valid_ip "${PIHOLE_DNS_2}" && [[ "${PIHOLE_DNS_2}" ]]; then PIHOLE_DNS_2=${strInvalid} fi # If either of the DNS servers are invalid, - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]] || [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]] || [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then # explain this to the user - whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\n\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} + whiptail --msgbox --backtitle "Invalid IP" --title "Invalid IP" "One or both entered IP addresses were invalid. Please try again.\\n\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c} # and set the variables back to nothing - if [[ ${PIHOLE_DNS_1} == "${strInvalid}" ]]; then + if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]]; then PIHOLE_DNS_1="" fi - if [[ ${PIHOLE_DNS_2} == "${strInvalid}" ]]; then + if [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then PIHOLE_DNS_2="" fi # Since the settings will not work, stay in the loop @@ -856,7 +857,7 @@ setDNS() { # Othwerise, else # Show the settings - if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\n DNS Server 1: $PIHOLE_DNS_1\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then + if (whiptail --backtitle "Specify Upstream DNS Provider(s)" --title "Upstream DNS Provider(s)" --yesno "Are these settings correct?\\n DNS Server 1: $PIHOLE_DNS_1\\n DNS Server 2: ${PIHOLE_DNS_2}" ${r} ${c}); then # and break from the loop since the servers are vaid DNSSettingsCorrect=True # Otherwise, @@ -878,7 +879,7 @@ setLogging() { local LogChoices # Ask if the user wants to log queries - LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) + LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?\\n (Disabling will render graphs on the Admin page useless):" ${r} ${c} 6) # The default selection is on LogChooseOptions=("On (Recommended)" "" on Off "" off) @@ -916,12 +917,12 @@ setAdminFlag() { # Depending on their choice case ${WebChoices} in "On (Recommended)") - echo -e " ${INFO} Web Interface On." + echo -e " ${INFO} Web Interface On" # Set it to true INSTALL_WEB=true ;; Off) - echo -e " ${INFO} Web Interface off." + echo -e " ${INFO} Web Interface Off" # or false INSTALL_WEB=false ;; @@ -939,7 +940,7 @@ version_check_dnsmasq() { local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf" # If the dnsmasq config file exists - if [ -f ${dnsmasq_conf} ]; then + if [[ -f "${dnsmasq_conf}" ]]; then echo -ne " ${INFO} Existing dnsmasq.conf found..." # If gravity.list is found within this file, we presume it's from older versions on Pi-hole, if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then @@ -1048,8 +1049,8 @@ installScripts() { # Otherwise, else # Show an error and exit - echo -e "${OVER} ${CROSS} ${str}" - echo -e " ${COL_LIGHT_RED}Error: Local repo ${PI_HOLE_LOCAL_REPO} not found, exiting installer${COL_NC}" + echo -e "${OVER} ${CROSS} ${str} + ${COL_LIGHT_RED}Error: Local repo ${PI_HOLE_LOCAL_REPO} not found, exiting installer${COL_NC}" exit 1 fi } @@ -1062,15 +1063,15 @@ installConfigs() { version_check_dnsmasq # If the user chose to install the dashboard, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # and if the Web server conf directory does not exist, - if [ ! -d "/etc/lighttpd" ]; then + if [[ ! -d "/etc/lighttpd" ]]; then # make it mkdir /etc/lighttpd # and set the owners chown "${USER}":root /etc/lighttpd # Otherwise, if the config file already exists - elif [ -f "/etc/lighttpd/lighttpd.conf" ]; then + elif [[ -f "/etc/lighttpd/lighttpd.conf" ]]; then # back up the original mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig fi @@ -1164,16 +1165,13 @@ update_package_cache() { notify_package_updates_available() { # Local, named variables local str="Checking ${PKG_MANAGER} for upgraded packages" - echo "" - echo -ne " ${INFO} ${str}..." + echo -ne "\\n ${INFO} ${str}..." # Store the list of packages in a variable updatesToInstall=$(eval "${PKG_COUNT}") - #echo -e "\r\033[K ${TICK} ${str}" - #echo "" - # + if [[ -d "/lib/modules/$(uname -r)" ]]; then # - if [[ ${updatesToInstall} -eq "0" ]]; then + if [[ "${updatesToInstall}" -eq 0 ]]; then # echo -e "${OVER} ${TICK} ${str}... up to date!" echo "" @@ -1184,10 +1182,8 @@ notify_package_updates_available() { echo "" fi else - # echo -e "${OVER} ${CROSS} ${str} - Kernel update detected. If the install fails, please reboot and try again" - echo "" + Kernel update detected. If the install fails, please reboot and try again\\n" fi } @@ -1199,7 +1195,7 @@ install_dependent_packages() { # Add one to the counter counter=$((counter+1)) # If it equals 1, - if [ ${counter} == 1 ]; then + if [[ "${counter}" == 1 ]]; then # echo -e " ${INFO} Installer Dependency checks..." else @@ -1233,7 +1229,7 @@ install_dependent_packages() { fi done # - if [[ ${#installArray[@]} -gt 0 ]]; then + if [[ "${#installArray[@]}" -gt 0 ]]; then # test_dpkg_lock # @@ -1258,7 +1254,7 @@ install_dependent_packages() { fi done # - if [[ ${#installArray[@]} -gt 0 ]]; then + if [[ "${#installArray[@]}" -gt 0 ]]; then # "${PKG_INSTALL[@]}" "${installArray[@]}" &> /dev/null return @@ -1273,7 +1269,7 @@ CreateLogFile() { echo "" echo -ne " ${INFO} ${str}..." # If the pihole log does not exist, - if [ ! -f /var/log/pihole.log ]; then + if [[ ! -f "/var/log/pihole.log" ]]; then # Make it, touch /var/log/pihole.log # set the permissions, @@ -1293,11 +1289,11 @@ installPiholeWeb() { echo "" echo " ${INFO} Installing blocking page..." # If the pihole Web directory exists, - if [ -d "/var/www/html/pihole" ]; then + if [[ -d "/var/www/html/pihole" ]]; then local str="Installing index.php" echo -ne " ${INFO} ${str}..." # and if the index file exists, - if [ -f "/var/www/html/pihole/index.php" ]; then + if [[ -f "/var/www/html/pihole/index.php" ]]; then # do not overwrite it, echo -e " ${COL_LIGHT_GREEN}detected index.php, not overwriting${COL_NC}" # if it doesn't exist @@ -1310,7 +1306,7 @@ installPiholeWeb() { local str="Installing index.js" echo -ne " ${INFO} ${str}..." # and if the index file exists, - if [ -f "/var/www/html/pihole/index.js" ]; then + if [[ -f "/var/www/html/pihole/index.js" ]]; then # do not overwrite it, echo -e " ${COL_LIGHT_GREEN}detected index.js, not overwriting${COL_NC}" else @@ -1322,7 +1318,7 @@ installPiholeWeb() { local str="Installing blockingpage.css" echo -ne " ${INFO} ${str}..." # and if the index file exists, - if [ -f "/var/www/html/pihole/blockingpage.css" ]; then + if [[ -f "/var/www/html/pihole/blockingpage.css" ]]; then # do not overwrite it, echo -e " ${COL_LIGHT_GREEN}detected blockingpage.css, not overwriting${COL_NC}" else @@ -1343,15 +1339,15 @@ installPiholeWeb() { local str="Backing up index.lighttpd.html" echo -ne " ${INFO} ${str}..." # If the default index file exists, - if [ -f /var/www/html/index.lighttpd.html ]; then + if [[ -f "/var/www/html/index.lighttpd.html" ]]; then # back it up mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig echo -e "${OVER} ${TICK} ${str}" # Othwerwise, else # don't do anything - echo -e "${OVER} ${CROSS} ${str}" - echo -e " No default index.lighttpd.html file found... not backing up" + echo -e "${OVER} ${CROSS} ${str} + No default index.lighttpd.html file found... not backing up" fi fi @@ -1435,9 +1431,9 @@ configureFirewall() { # If a firewall is running, if firewall-cmd --state &> /dev/null; then # ask if the user wants to install Pi-hole's default firwall rules - whiptail --title "Firewall in use" --yesno "We have detected a running firewall\n\nPi-hole currently requires HTTP and DNS port access.\n\n\n\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ + whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ { echo -e " ${INFO} Not installing firewall rulesets."; return 0; } - echo -e " ${TICK} Configuring FirewallD for httpd and dnsmasq." + echo -e " ${TICK} Configuring FirewallD for httpd and dnsmasq" # Allow HTTP and DNS traffice firewall-cmd --permanent --add-service=http --add-service=dns # Reload the firewall to apply these changes @@ -1448,9 +1444,9 @@ configureFirewall() { # If chain Policy is not ACCEPT or last Rule is not ACCEPT # then check and insert our Rules above the DROP/REJECT Rule. if iptables -S INPUT | head -n1 | grep -qv '^-P.*ACCEPT$' || iptables -S INPUT | tail -n1 | grep -qv '^-\(A\|P\).*ACCEPT$'; then - whiptail --title "Firewall in use" --yesno "We have detected a running firewall\n\nPi-hole currently requires HTTP and DNS port access.\n\n\n\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ + whiptail --title "Firewall in use" --yesno "We have detected a running firewall\\n\\nPi-hole currently requires HTTP and DNS port access.\\n\\n\\n\\nInstall Pi-hole default firewall rules?" ${r} ${c} || \ { echo -e " ${INFO} Not installing firewall rulesets."; return 0; } - echo -e " ${TICK} Installing new IPTables firewall rulesets." + echo -e " ${TICK} Installing new IPTables firewall rulesets" # Check chain first, otherwise a new rule will duplicate old ones iptables -C INPUT -p tcp -m tcp --dport 80 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 80 -j ACCEPT iptables -C INPUT -p tcp -m tcp --dport 53 -j ACCEPT &> /dev/null || iptables -I INPUT 1 -p tcp -m tcp --dport 53 -j ACCEPT @@ -1461,31 +1457,32 @@ configureFirewall() { # Othwerwise, else # no firewall is running - echo -e " ${INFO} No active firewall detected.. skipping firewall configuration." + echo -e " ${INFO} No active firewall detected.. skipping firewall configuration" # so just exit return 0 fi - echo -e " ${INFO} Skipping firewall configuration." + echo -e " ${INFO} Skipping firewall configuration" } # finalExports() { # If the Web interface is not set to be installed, - if [[ ${INSTALL_WEB} == false ]]; then + if [[ "${INSTALL_WEB}" == false ]]; then # and if there is not an IPv4 address, - if [ ${IPV4_ADDRESS} ]; then + if [[ "${IPV4_ADDRESS}" ]]; then # there is no block page, so set IPv4 to 0.0.0.0 (all IP addresses) IPV4_ADDRESS="0.0.0.0" fi - if [ ${IPV6_ADDRESS} ]; then + if [[ "${IPV6_ADDRESS}" ]]; then # and IPv6 to ::/0 IPV6_ADDRESS="::/0" fi fi # If the setup variable file exists, - if [ -e "${setupVars}" ]; then - sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;/INSTALL_WEB/d;/LIGHTTPD_ENABLED/d;' "${setupVars}" + if [[ -e "${setupVars}" ]]; then + # update the variables in the file + sed -i.update.bak '/PIHOLE_INTERFACE/d;/IPV4_ADDRESS/d;/IPV6_ADDRESS/d;/PIHOLE_DNS_1/d;/PIHOLE_DNS_2/d;/QUERY_LOGGING/d;/INSTALL_WEB/d;' "${setupVars}" fi # echo the information to the user { @@ -1526,7 +1523,7 @@ installLogrotate() { # the local properties of the /var/log directory logusergroup="$(stat -c '%U %G' /var/log)" # If the variable has a value, - if [[ ! -z $logusergroup ]]; then + if [[ ! -z "${logusergroup}" ]]; then # sed -i "s/# su #/su ${logusergroup}/g;" /etc/pihole/logrotate fi @@ -1539,8 +1536,8 @@ installPihole() { create_pihole_user # If the user wants to install the Web interface, - if [[ ${INSTALL_WEB} == true ]]; then - if [ ! -d "/var/www/html" ]; then + if [[ "${INSTALL_WEB}" == true ]]; then + if [[ ! -d "/var/www/html" ]]; then # make the Web directory if necessary mkdir -p /var/www/html fi @@ -1550,13 +1547,13 @@ installPihole() { # Give pihole access to the Web server group usermod -a -G ${LIGHTTPD_GROUP} pihole # If the lighttpd command is executable, - if [ -x "$(command -v lighty-enable-mod)" ]; then + if [[ -x "$(command -v lighty-enable-mod)" ]]; then # enable fastcgi and fastcgi-php lighty-enable-mod fastcgi fastcgi-php > /dev/null || true else # Othweise, show info about installing them echo -e " ${INFO} Warning: 'lighty-enable-mod' utility not found - Please ensure fastcgi is enabled if you experience issues\n" + Please ensure fastcgi is enabled if you experience issues\\n" fi fi # Install scripts, @@ -1566,7 +1563,7 @@ installPihole() { # and create the log file CreateLogFile # If the user wants to install the dashboard, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # do so installPiholeWeb fi @@ -1575,7 +1572,7 @@ installPihole() { # Install the logrotate file installLogrotate # Check if FTL is installed - FTLdetect || echo -e " ${CROSS} FTL Engine not installed." + FTLdetect || echo -e " ${CROSS} FTL Engine not installed" # Configure the firewall configureFirewall @@ -1604,7 +1601,7 @@ updatePihole() { # Create the log file CreateLogFile # If the user wants to install the dasboard, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # do so installPiholeWeb fi @@ -1625,19 +1622,17 @@ updatePihole() { checkSelinux() { # If the getenforce command exists, if command -v getenforce &> /dev/null; then - echo "" - echo -ne " ${INFO} SELinux Support Detected... Mode: " # Store the current mode in a variable enforceMode=$(getenforce) - echo "${enforceMode}" + echo -e "\\n ${INFO} SELinux mode detected: ${enforceMode}" + # If it's enforcing, if [[ "${enforceMode}" == "Enforcing" ]]; then # Explain Pi-hole does not support it yet - whiptail --title "SELinux Enforcing Detected" --yesno "SELinux is being Enforced on your system!\n\nPi-hole currently does not support SELinux, but you may still continue with the installation.\n\nNote: Admin UI Will not function fully without setting your policies correctly\n\nContinue installing Pi-hole?" ${r} ${c} || \ - { echo ""; echo -e " ${COL_LIGHT_RED}SELinux Enforcing detected, exiting installer${COL_NC}"; exit 1; } - echo "" - echo -e " ${INFO} Continuing installation with SELinux Enforcing" - echo -e " ${INFO} Please refer to official SELinux documentation to create a custom policy" + whiptail --defaultno --title "SELinux Enforcing Detected" --yesno "SELinux is being ENFORCED on your system! \\n\\nPi-hole currently does not support SELinux, but you may still continue with the installation.\\n\\nNote: Web Admin will not be fully functional unless you set your policies correctly\\n\\nContinue installing Pi-hole?" ${r} ${c} || \ + { echo -e "\\n ${COL_LIGHT_RED}SELinux Enforcing detected, exiting installer${COL_NC}"; exit 1; } + echo -e " ${INFO} Continuing installation with SELinux Enforcing + ${INFO} Please refer to official SELinux documentation to create a custom policy" fi fi } @@ -1645,7 +1640,7 @@ checkSelinux() { # Installation complete message with instructions for the user displayFinalMessage() { # If - if [[ ${#1} -gt 0 ]] ; then + if [[ "${#1}" -gt 0 ]] ; then pwstring="$1" # else, if the dashboard password in the setup variables exists, elif [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) -gt 0 ]]; then @@ -1656,7 +1651,7 @@ displayFinalMessage() { pwstring="NOT SET" fi # If the user wants to install the dashboard, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # Store a message in a variable and display it additional="View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin @@ -1678,7 +1673,7 @@ ${additional}" ${r} ${c} update_dialogs() { # If pihole -r "reconfigure" option was selected, - if [ "${reconfigure}" = true ]; then + if [[ "${reconfigure}" = true ]]; then # set some variables that will be used opt1a="Repair" opt1b="This will retain existing settings" @@ -1694,7 +1689,7 @@ update_dialogs() { opt2b="This will allow you to enter new settings" # Display the information to the user - UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\n\nWe have detected an existing install.\n\nPlease choose from the following options: \n($strAdd)" ${r} ${c} 2 \ + UpdateCmd=$(whiptail --title "Existing Install Detected!" --menu "\\n\\nWe have detected an existing install.\\n\\nPlease choose from the following options: \\n($strAdd)" ${r} ${c} 2 \ "${opt1a}" "${opt1b}" \ "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) || \ { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; } @@ -1703,7 +1698,7 @@ update_dialogs() { case ${UpdateCmd} in # repair, or ${opt1a}) - echo -e " ${INFO} ${opt1a} option selected." + echo -e " ${INFO} ${opt1a} option selected" useUpdateVars=true ;; # recongigure, @@ -1714,7 +1709,6 @@ update_dialogs() { esac } - clone_or_update_repos() { # If the user wants to reconfigure, if [[ "${reconfigure}" == true ]]; then @@ -1725,7 +1719,7 @@ clone_or_update_repos() { exit 1; \ } # If the Web interface was installed, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # reset it's repo resetRepo ${webInterfaceDir} || \ { echo -e " ${COL_LIGHT_RED}Unable to reset ${webInterfaceDir}, exiting installer${COL_NC}"; \ @@ -1736,11 +1730,11 @@ clone_or_update_repos() { else # so get git files for Core getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || \ - { echo "!!! Unable to clone ${piholeGitUrl} into ${PI_HOLE_LOCAL_REPO}, unable to continue."; \ + { echo -e " ${COL_LIGHT_RED}Unable to clone ${piholeGitUrl} into ${PI_HOLE_LOCAL_REPO}, unable to continue${COL_NC}"; \ exit 1; \ } # If the Web interface was installed, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # get the Web git files getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || \ { echo -e " ${COL_LIGHT_RED}Unable to clone ${webInterfaceGitUrl} into ${webInterfaceDir}, exiting installer${COL_NC}"; \ @@ -1824,21 +1818,23 @@ FTLdetect() { local str="Detecting architecture" echo -ne " ${INFO} ${str}..." # If the machine is arm or aarch - if [[ ${machine} == arm* || ${machine} == *aarch* ]]; then + if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then # ARM # - local rev=$(uname -m | sed "s/[^0-9]//g;") + local rev + rev=$(uname -m | sed "s/[^0-9]//g;") # - local lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') + local lib + lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }') # - if [[ "$lib" == "/lib/ld-linux-aarch64.so.1" ]]; then + if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture" # set the binary to be used binary="pihole-FTL-aarch64-linux-gnu" # - elif [[ "$lib" == "/lib/ld-linux-armhf.so.3" ]]; then + elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then # - if [ "$rev" -gt "6" ]; then + if [[ "${rev}" -gt 6 ]]; then echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv7+)" # set the binary to be used binary="pihole-FTL-arm-linux-gnueabihf" @@ -1853,22 +1849,22 @@ FTLdetect() { # set the binary to be used binary="pihole-FTL-arm-linux-gnueabi" fi - elif [[ $machine == ppc ]]; then + elif [[ "${machine}" == "ppc" ]]; then # PowerPC - echo "::: Detected PowerPC architecture" + echo -e "${OVER} ${TICK} Detected PowerPC architecture" # set the binary to be used binary="pihole-FTL-powerpc-linux-gnu" - elif [[ ${machine} == x86_64 ]]; then + elif [[ "${machine}" == "x86_64" ]]; then # 64bit echo -e "${OVER} ${TICK} Detected x86_64 architecture" # set the binary to be used binary="pihole-FTL-linux-x86_64" else # Something else - we try to use 32bit executable and warn the user - if [[ ! ${machine} == i686 ]]; then + if [[ ! "${machine}" == "i686" ]]; then echo -e "${OVER} ${CROSS} ${str}... - ${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable - Contact support if you experience issues (e.g: FTL not running)${COL_NC}" + ${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable${COL_NC} + Contact Pi-hole Support if you experience issues (e.g: FTL not running)" else echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture" fi @@ -1910,7 +1906,6 @@ FTLdetect() { } main() { - ######## FIRST CHECK ######## # Show the Pi-hole logo so people know it's genuine since the logo and name are trademarked show_ascii_berry @@ -1919,18 +1914,17 @@ main() { echo "" # If the user's id is zero, - if [[ ${EUID} -eq 0 ]]; then + if [[ "${EUID}" -eq 0 ]]; then # they are root and all is good echo -e " ${TICK} ${str}" # Otherwise, else # They do not have enough privileges, so let the user know echo -e " ${CROSS} ${str} - Script called with non-root privileges - The Pi-hole requires elevated privleges to install and run - Please check the installer for any concerns regarding this requirement - Make sure to download this script from a trusted source" - echo "" + ${COL_LIGHT_RED}Script called with non-root privileges${COL_NC} + The Pi-hole requires elevated privleges to install and run + Please check the installer for any concerns regarding this requirement + Make sure to download this script from a trusted source\\n" echo -ne " ${INFO} Sudo utility check" # If the sudo command exists, @@ -1943,7 +1937,7 @@ main() { else # Let them know they need to run it as root echo -e "${OVER} ${CROSS} Sudo utility check - Sudo is needed for the Web Interface to run pihole commands\n + Sudo is needed for the Web Interface to run pihole commands\\n ${COL_LIGHT_RED}Please re-run this installer as root${COL_NC}" exit 1 fi @@ -1962,7 +1956,7 @@ main() { done # If the setup variable file exists, - if [[ -f ${setupVars} ]]; then + if [[ -f "${setupVars}" ]]; then # if it's running unattended, if [[ "${runUnattended}" == true ]]; then echo -e " ${INFO} Performing unattended setup, no whiptail dialogs will be displayed" @@ -1995,15 +1989,14 @@ main() { # Check if SELinux is Enforcing checkSelinux - - if [[ ${useUpdateVars} == false ]]; then + if [[ "${useUpdateVars}" == false ]]; then # Display welcome dialogs welcomeDialogs # Create directory for Pi-hole storage mkdir -p /etc/pihole/ stop_service dnsmasq - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then stop_service lighttpd fi # Determine available interfaces @@ -2022,7 +2015,7 @@ main() { clone_or_update_repos # Install packages used by the Pi-hole - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # Install the Web dependencies DEPS=("${PIHOLE_DEPS[@]}" "${PIHOLE_WEB_DEPS[@]}") # Otherwise, @@ -2046,11 +2039,11 @@ main() { # Clone/Update the repos clone_or_update_repos - # Source ${setupVars} for use in the rest of the functions. + # Source ${setupVars} for use in the rest of the functions source ${setupVars} # Install packages used by the Pi-hole - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # Install the Web dependencies DEPS=("${PIHOLE_DEPS[@]}" "${PIHOLE_WEB_DEPS[@]}") # Otherwise, @@ -2074,13 +2067,14 @@ main() { # Move the log file into /etc/pihole for storage mv ${tmpLog} ${instalLogLoc} - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # Add password to web UI if there is none pw="" # If no password is set, if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then # generate a random password pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8) + # shellcheck disable=SC1091 . /opt/pihole/webpage.sh echo "WEBPASSWORD=$(HashPassword ${pw})" >> ${setupVars} fi @@ -2115,20 +2109,19 @@ main() { fi # If the Web interface was installed, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then # If there is a password, if (( ${#pw} > 0 )) ; then # display the password echo -e " ${INFO} Web Interface password: ${COL_LIGHT_GREEN}${pw}${COL_NC} - This can be changed using 'pihole -a -p'" - echo "" + This can be changed using 'pihole -a -p'\\n" fi fi # if [[ "${useUpdateVars}" == false ]]; then # If the Web interface was installed, - if [[ ${INSTALL_WEB} == true ]]; then + if [[ "${INSTALL_WEB}" == true ]]; then echo -e " View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin" echo "" fi @@ -2145,7 +2138,7 @@ main() { fi # Display where the log file is - echo -e "\n ${INFO} The install log is located at: /etc/pihole/install.log + echo -e "\\n ${INFO} The install log is located at: /etc/pihole/install.log ${COL_LIGHT_GREEN}${INSTALL_TYPE} Complete! ${COL_NC}" } diff --git a/test/test_automated_install.py b/test/test_automated_install.py index 9129c314..7f1a86a1 100644 --- a/test/test_automated_install.py +++ b/test/test_automated_install.py @@ -80,7 +80,7 @@ def test_configureFirewall_firewalld_running_no_errors(Pihole): source /opt/pihole/basic-install.sh configureFirewall ''') - expected_stdout = 'Configuring FirewallD for httpd and dnsmasq.' + expected_stdout = 'Configuring FirewallD for httpd and dnsmasq' assert expected_stdout in configureFirewall.stdout firewall_calls = Pihole.run('cat /var/log/firewall-cmd').stdout assert 'firewall-cmd --state' in firewall_calls @@ -95,7 +95,7 @@ def test_configureFirewall_firewalld_disabled_no_errors(Pihole): source /opt/pihole/basic-install.sh configureFirewall ''') - expected_stdout = 'No active firewall detected.. skipping firewall configuration.' + expected_stdout = 'No active firewall detected.. skipping firewall configuration' assert expected_stdout in configureFirewall.stdout def test_configureFirewall_firewalld_enabled_declined_no_errors(Pihole): @@ -444,7 +444,7 @@ def test_IPv6_only_link_local(Pihole): source /opt/pihole/basic-install.sh useIPv6dialog ''') - expected_stdout = 'Found neither IPv6 ULA nor GUA address, blocking IPv6 ads will not be enabled' + expected_stdout = 'Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled' assert expected_stdout in detectPlatform.stdout def test_IPv6_only_ULA(Pihole):