Advertisement
Guest User

Untitled

a guest
Dec 7th, 2018
244
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 104.29 KB | None | 0 0
  1. #!/usr/bin/env bash
  2. # shellcheck disable=SC1090
  3.  
  4. # Pi-hole: A black hole for Internet advertisements
  5. # (c) 2017-2018 Pi-hole, LLC (https://pi-hole.net)
  6. # Network-wide ad blocking via your own hardware.
  7. #
  8. # Installs and Updates Pi-hole
  9. #
  10. # This file is copyright under the latest version of the EUPL.
  11. # Please see LICENSE file for your rights under this license.
  12.  
  13. # pi-hole.net/donate
  14. #
  15. # Install with this command (from your Linux machine):
  16. #
  17. # curl -sSL https://install.pi-hole.net | bash
  18.  
  19. # -e option instructs bash to immediately exit if any command [1] has a non-zero exit status
  20. # We do not want users to end up with a partially working install, so we exit the script
  21. # instead of continuing the installation with something broken
  22. set -e
  23.  
  24. ######## VARIABLES #########
  25. # For better maintainability, we store as much information that can change in variables
  26. # This allows us to make a change in one place that can propagate to all instances of the variable
  27. # These variables should all be GLOBAL variables, written in CAPS
  28. # Local variables will be in lowercase and will exist only within functions
  29. # It's still a work in progress, so you may see some variance in this guideline until it is complete
  30.  
  31. # Location for final installation log storage
  32. installLogLoc=/etc/pihole/install.log
  33. # This is an important file as it contains information specific to the machine it's being installed on
  34. setupVars=/etc/pihole/setupVars.conf
  35. # Pi-hole uses lighttpd as a Web server, and this is the config file for it
  36. # shellcheck disable=SC2034
  37. lighttpdConfig=/etc/lighttpd/lighttpd.conf
  38. # This is a file used for the colorized output
  39. coltable=/opt/pihole/COL_TABLE
  40.  
  41. # We store several other folders and
  42. webInterfaceGitUrl="https://github.com/pi-hole/AdminLTE.git"
  43. webInterfaceDir="/var/www/html/admin"
  44. piholeGitUrl="https://github.com/pi-hole/pi-hole.git"
  45. PI_HOLE_LOCAL_REPO="/etc/.pihole"
  46. # These are the names of pi-holes files, stored in an array
  47. PI_HOLE_FILES=(chronometer list piholeDebug piholeLogFlush setupLCD update version gravity uninstall webpage)
  48. # This folder is where the Pi-hole scripts will be installed
  49. PI_HOLE_INSTALL_DIR="/opt/pihole"
  50. PI_HOLE_CONFIG_DIR="/etc/pihole"
  51. useUpdateVars=false
  52.  
  53. adlistFile="/etc/pihole/adlists.list"
  54. regexFile="/etc/pihole/regex.list"
  55. # Pi-hole needs an IP address; to begin, these variables are empty since we don't know what the IP is until
  56. # this script can run
  57. IPV4_ADDRESS=""
  58. IPV6_ADDRESS=""
  59. # By default, query logging is enabled and the dashboard is set to be installed
  60. QUERY_LOGGING=true
  61. INSTALL_WEB_INTERFACE=true
  62.  
  63. if [ -z "${USER}" ]; then
  64. USER="$(id -un)"
  65. fi
  66.  
  67.  
  68. # Find the rows and columns will default to 80x24 if it can not be detected
  69. screen_size=$(stty size 2>/dev/null || echo 24 80)
  70. rows=$(echo "${screen_size}" | awk '{print $1}')
  71. columns=$(echo "${screen_size}" | awk '{print $2}')
  72.  
  73. # Divide by two so the dialogs take up half of the screen, which looks nice.
  74. r=$(( rows / 2 ))
  75. c=$(( columns / 2 ))
  76. # Unless the screen is tiny
  77. r=$(( r < 20 ? 20 : r ))
  78. c=$(( c < 70 ? 70 : c ))
  79.  
  80. ######## Undocumented Flags. Shhh ########
  81. # These are undocumented flags; some of which we can use when repairing an installation
  82. # The runUnattended flag is one example of this
  83. skipSpaceCheck=false
  84. reconfigure=false
  85. runUnattended=false
  86. INSTALL_WEB_SERVER=true
  87. # Check arguments for the undocumented flags
  88. for var in "$@"; do
  89. case "$var" in
  90. "--reconfigure" ) reconfigure=true;;
  91. "--i_do_not_follow_recommendations" ) skipSpaceCheck=true;;
  92. "--unattended" ) runUnattended=true;;
  93. "--disable-install-webserver" ) INSTALL_WEB_SERVER=false;;
  94. esac
  95. done
  96.  
  97. # If the color table file exists,
  98. if [[ -f "${coltable}" ]]; then
  99. # source it
  100. source ${coltable}
  101. # Otherwise,
  102. else
  103. # Set these values so the installer can still run in color
  104. COL_NC='\e[0m' # No Color
  105. COL_LIGHT_GREEN='\e[1;32m'
  106. COL_LIGHT_RED='\e[1;31m'
  107. TICK="[${COL_LIGHT_GREEN}✓${COL_NC}]"
  108. CROSS="[${COL_LIGHT_RED}✗${COL_NC}]"
  109. INFO="[i]"
  110. # shellcheck disable=SC2034
  111. DONE="${COL_LIGHT_GREEN} done!${COL_NC}"
  112. OVER="\\r\\033[K"
  113. fi
  114.  
  115. # A simple function that just echoes out our logo in ASCII format
  116. # This lets users know that it is a Pi-hole, LLC product
  117. show_ascii_berry() {
  118. echo -e "
  119. ${COL_LIGHT_GREEN}.;;,.
  120. .ccccc:,.
  121. :cccclll:. ..,,
  122. :ccccclll. ;ooodc
  123. 'ccll:;ll .oooodc
  124. .;cll.;;looo:.
  125. ${COL_LIGHT_RED}.. ','.
  126. .',,,,,,'.
  127. .',,,,,,,,,,.
  128. .',,,,,,,,,,,,....
  129. ....''',,,,,,,'.......
  130. ......... .... .........
  131. .......... ..........
  132. .......... ..........
  133. ......... .... .........
  134. ........,,,,,,,'......
  135. ....',,,,,,,,,,,,.
  136. .',,,,,,,,,'.
  137. .',,,,,,'.
  138. ..'''.${COL_NC}
  139. "
  140. }
  141.  
  142. # Compatibility
  143. distro_check() {
  144. # If apt-get is installed, then we know it's part of the Debian family
  145. if command -v apt-get &> /dev/null; then
  146. # Set some global variables here
  147. # We don't set them earlier since the family might be Red Hat, so these values would be different
  148. PKG_MANAGER="apt-get"
  149. # A variable to store the command used to update the package cache
  150. UPDATE_PKG_CACHE="${PKG_MANAGER} update"
  151. # An array for something...
  152. PKG_INSTALL=(${PKG_MANAGER} --yes --no-install-recommends install)
  153. # grep -c will return 1 retVal on 0 matches, block this throwing the set -e with an OR TRUE
  154. PKG_COUNT="${PKG_MANAGER} -s -o Debug::NoLocking=true upgrade | grep -c ^Inst || true"
  155. # Some distros vary slightly so these fixes for dependencies may apply
  156. # Debian 7 doesn't have iproute2 so if the dry run install is successful,
  157. if ${PKG_MANAGER} install --dry-run iproute2 > /dev/null 2>&1; then
  158. # we can install it
  159. iproute_pkg="iproute2"
  160. # Otherwise,
  161. else
  162. # use iproute
  163. iproute_pkg="iproute"
  164. fi
  165. # Check for and determine version number (major and minor) of current php install
  166. if command -v php &> /dev/null; then
  167. phpInsVersion="$(php -v | head -n1 | grep -Po '(?<=PHP )[^ ]+')"
  168. echo -e " ${INFO} Existing PHP installation detected : PHP version $phpInsVersion"
  169. phpInsMajor="$(echo "$phpInsVersion" | cut -d\. -f1)"
  170. phpInsMinor="$(echo "$phpInsVersion" | cut -d\. -f2)"
  171. # Is installed php version 7.0 or greater
  172. if [ "$(echo "$phpInsMajor.$phpInsMinor < 7.0" | bc )" == 0 ]; then
  173. phpInsNewer=true
  174. fi
  175. fi
  176. # Check if installed php is v 7.0, or newer to determine packages to install
  177. if [[ "$phpInsNewer" != true ]]; then
  178. # Prefer the php metapackage if it's there
  179. if ${PKG_MANAGER} install --dry-run php > /dev/null 2>&1; then
  180. phpVer="php"
  181. # fall back on the php5 packages
  182. else
  183. phpVer="php5"
  184. fi
  185. else
  186. # Newer php is installed, its common, cgi & sqlite counterparts are deps
  187. phpVer="php$phpInsMajor.$phpInsMinor"
  188. fi
  189. # We also need the correct version for `php-sqlite` (which differs across distros)
  190. if ${PKG_MANAGER} install --dry-run ${phpVer}-sqlite3 > /dev/null 2>&1; then
  191. phpSqlite="sqlite3"
  192. else
  193. phpSqlite="sqlite"
  194. fi
  195. # Since our install script is so large, we need several other programs to successfully get a machine provisioned
  196. # These programs are stored in an array so they can be looped through later
  197. INSTALLER_DEPS=(apt-utils dialog debconf dhcpcd5 git ${iproute_pkg} whiptail)
  198. # Pi-hole itself has several dependencies that also need to be installed
  199. PIHOLE_DEPS=(bc cron curl dnsutils iputils-ping lsof netcat psmisc sudo unzip wget idn2 sqlite3 libcap2-bin dns-root-data resolvconf)
  200. # The Web dashboard has some that also need to be installed
  201. # It's useful to separate the two since our repos are also setup as "Core" code and "Web" code
  202. PIHOLE_WEB_DEPS=(lighttpd ${phpVer}-common ${phpVer}-cgi ${phpVer}-${phpSqlite})
  203. # The Web server user,
  204. LIGHTTPD_USER="www-data"
  205. # group,
  206. LIGHTTPD_GROUP="www-data"
  207. # and config file
  208. LIGHTTPD_CFG="lighttpd.conf.debian"
  209.  
  210. # A function to check...
  211. test_dpkg_lock() {
  212. # An iterator used for counting loop iterations
  213. i=0
  214. # fuser is a program to show which processes use the named files, sockets, or filesystems
  215. # So while the command is true
  216. while fuser /var/lib/dpkg/lock >/dev/null 2>&1 ; do
  217. # Wait half a second
  218. sleep 0.5
  219. # and increase the iterator
  220. ((i=i+1))
  221. done
  222. # Always return success, since we only return if there is no
  223. # lock (anymore)
  224. return 0
  225. }
  226.  
  227. # If apt-get is not found, check for rpm to see if it's a Red Hat family OS
  228. elif command -v rpm &> /dev/null; then
  229. # Then check if dnf or yum is the package manager
  230. if command -v dnf &> /dev/null; then
  231. PKG_MANAGER="dnf"
  232. else
  233. PKG_MANAGER="yum"
  234. fi
  235.  
  236. # Fedora and family update cache on every PKG_INSTALL call, no need for a separate update.
  237. UPDATE_PKG_CACHE=":"
  238. PKG_INSTALL=(${PKG_MANAGER} install -y)
  239. PKG_COUNT="${PKG_MANAGER} check-update | egrep '(.i686|.x86|.noarch|.arm|.src)' | wc -l"
  240. INSTALLER_DEPS=(dialog git iproute net-tools newt procps-ng which)
  241. PIHOLE_DEPS=(bc bind-utils cronie curl findutils nmap-ncat sudo unzip wget libidn2 psmisc)
  242. PIHOLE_WEB_DEPS=(lighttpd lighttpd-fastcgi php-common php-cli php-pdo)
  243. LIGHTTPD_USER="lighttpd"
  244. LIGHTTPD_GROUP="lighttpd"
  245. LIGHTTPD_CFG="lighttpd.conf.fedora"
  246. # If the host OS is Fedora,
  247. if grep -qi 'fedora' /etc/redhat-release; then
  248. # all required packages should be available by default with the latest fedora release
  249. # ensure 'php-json' is installed on Fedora (installed as dependency on CentOS7 + Remi repository)
  250. PIHOLE_WEB_DEPS+=('php-json')
  251. # or if host OS is CentOS,
  252. elif grep -qi 'centos' /etc/redhat-release; then
  253. # Pi-Hole currently supports CentOS 7+ with PHP7+
  254. SUPPORTED_CENTOS_VERSION=7
  255. SUPPORTED_CENTOS_PHP_VERSION=7
  256. # Check current CentOS major release version
  257. CURRENT_CENTOS_VERSION=$(rpm -q --queryformat '%{VERSION}' centos-release)
  258. # Check if CentOS version is supported
  259. if [[ $CURRENT_CENTOS_VERSION -lt $SUPPORTED_CENTOS_VERSION ]]; then
  260. echo -e " ${CROSS} CentOS $CURRENT_CENTOS_VERSION is not suported."
  261. echo -e " Please update to CentOS release $SUPPORTED_CENTOS_VERSION or later"
  262. # exit the installer
  263. exit
  264. fi
  265. # on CentOS we need to add the EPEL repository to gain access to Fedora packages
  266. EPEL_PKG="epel-release"
  267. rpm -q ${EPEL_PKG} &> /dev/null || rc=$?
  268. if [[ $rc -ne 0 ]]; then
  269. echo -e " ${INFO} Enabling EPEL package repository (https://fedoraproject.org/wiki/EPEL)"
  270. "${PKG_INSTALL[@]}" ${EPEL_PKG} &> /dev/null
  271. echo -e " ${TICK} Installed ${EPEL_PKG}"
  272. fi
  273.  
  274. # The default php on CentOS 7.x is 5.4 which is EOL
  275. # Check if the version of PHP available via installed repositories is >= to PHP 7
  276. AVAILABLE_PHP_VERSION=$(${PKG_MANAGER} info php | grep -i version | grep -o '[0-9]\+' | head -1)
  277. if [[ $AVAILABLE_PHP_VERSION -ge $SUPPORTED_CENTOS_PHP_VERSION ]]; then
  278. # Since PHP 7 is available by default, install via default PHP package names
  279. : # do nothing as PHP is current
  280. else
  281. REMI_PKG="remi-release"
  282. REMI_REPO="remi-php72"
  283. rpm -q ${REMI_PKG} &> /dev/null || rc=$?
  284. if [[ $rc -ne 0 ]]; then
  285. # The PHP version available via default repositories is older than version 7
  286. if ! whiptail --defaultno --title "PHP 7 Update (recommended)" --yesno "PHP 7.x is recommended for both security and language features.\\nWould you like to install PHP7 via Remi's RPM repository?\\n\\nSee: https://rpms.remirepo.net for more information" ${r} ${c}; then
  287. # User decided to NOT update PHP from REMI, attempt to install the default available PHP version
  288. echo -e " ${INFO} User opt-out of PHP 7 upgrade on CentOS. Deprecated PHP may be in use."
  289. : # continue with unsupported php version
  290. else
  291. echo -e " ${INFO} Enabling Remi's RPM repository (https://rpms.remirepo.net)"
  292. "${PKG_INSTALL[@]}" "https://rpms.remirepo.net/enterprise/${REMI_PKG}-$(rpm -E '%{rhel}').rpm" &> /dev/null
  293. # enable the PHP 7 repository via yum-config-manager (provided by yum-utils)
  294. "${PKG_INSTALL[@]}" "yum-utils" &> /dev/null
  295. yum-config-manager --enable ${REMI_REPO} &> /dev/null
  296. echo -e " ${TICK} Remi's RPM repository has been enabled for PHP7"
  297. # trigger an install/update of PHP to ensure previous version of PHP is updated from REMI
  298. if "${PKG_INSTALL[@]}" "php-cli" &> /dev/null; then
  299. echo -e " ${TICK} PHP7 installed/updated via Remi's RPM repository"
  300. else
  301. echo -e " ${CROSS} There was a problem updating to PHP7 via Remi's RPM repository"
  302. exit 1
  303. fi
  304. fi
  305. fi
  306. fi
  307. else
  308. # If not a supported version of Fedora or CentOS,
  309. echo -e " ${CROSS} Unsupported RPM based distribution"
  310. # exit the installer
  311. exit
  312. fi
  313.  
  314. # If neither apt-get or rmp/dnf are found
  315. else
  316. # it's not an OS we can support,
  317. echo -e " ${CROSS} OS distribution not supported"
  318. # so exit the installer
  319. exit
  320. fi
  321. }
  322.  
  323. # A function for checking if a folder is a git repository
  324. is_repo() {
  325. # Use a named, local variable instead of the vague $1, which is the first argument passed to this function
  326. # These local variables should always be lowercase
  327. local directory="${1}"
  328. # A local variable for the current directory
  329. local curdir
  330. # A variable to store the return code
  331. local rc
  332. # Assign the current directory variable by using pwd
  333. curdir="${PWD}"
  334. # If the first argument passed to this function is a directory,
  335. if [[ -d "${directory}" ]]; then
  336. # move into the directory
  337. cd "${directory}"
  338. # Use git to check if the folder is a repo
  339. # git -C is not used here to support git versions older than 1.8.4
  340. git status --short &> /dev/null || rc=$?
  341. # If the command was not successful,
  342. else
  343. # Set a non-zero return code if directory does not exist
  344. rc=1
  345. fi
  346. # Move back into the directory the user started in
  347. cd "${curdir}"
  348. # Return the code; if one is not set, return 0
  349. return "${rc:-0}"
  350. }
  351.  
  352. # A function to clone a repo
  353. make_repo() {
  354. # Set named variables for better readability
  355. local directory="${1}"
  356. local remoteRepo="${2}"
  357. # The message to display when this function is running
  358. str="Clone ${remoteRepo} into ${directory}"
  359. # Display the message and use the color table to preface the message with an "info" indicator
  360. echo -ne " ${INFO} ${str}..."
  361. # If the directory exists,
  362. if [[ -d "${directory}" ]]; then
  363. # delete everything in it so git can clone into it
  364. rm -rf "${directory}"
  365. fi
  366. # Clone the repo and return the return code from this command
  367. git clone -q --depth 1 "${remoteRepo}" "${directory}" &> /dev/null || return $?
  368. # Show a colored message showing it's status
  369. echo -e "${OVER} ${TICK} ${str}"
  370. # Always return 0? Not sure this is correct
  371. return 0
  372. }
  373.  
  374. # We need to make sure the repos are up-to-date so we can effectively install Clean out the directory if it exists for git to clone into
  375. update_repo() {
  376. # Use named, local variables
  377. # As you can see, these are the same variable names used in the last function,
  378. # but since they are local, their scope does not go beyond this function
  379. # This helps prevent the wrong value from being assigned if you were to set the variable as a GLOBAL one
  380. local directory="${1}"
  381. local curdir
  382.  
  383. # A variable to store the message we want to display;
  384. # Again, it's useful to store these in variables in case we need to reuse or change the message;
  385. # we only need to make one change here
  386. local str="Update repo in ${1}"
  387.  
  388. # Make sure we know what directory we are in so we can move back into it
  389. curdir="${PWD}"
  390. # Move into the directory that was passed as an argument
  391. cd "${directory}" &> /dev/null || return 1
  392. # Let the user know what's happening
  393. echo -ne " ${INFO} ${str}..."
  394. # Stash any local commits as they conflict with our working code
  395. git stash --all --quiet &> /dev/null || true # Okay for stash failure
  396. git clean --quiet --force -d || true # Okay for already clean directory
  397. # Pull the latest commits
  398. git pull --quiet &> /dev/null || return $?
  399. # Show a completion message
  400. echo -e "${OVER} ${TICK} ${str}"
  401. # Move back into the original directory
  402. cd "${curdir}" &> /dev/null || return 1
  403. return 0
  404. }
  405.  
  406. # A function that combines the functions previously made
  407. getGitFiles() {
  408. # Setup named variables for the git repos
  409. # We need the directory
  410. local directory="${1}"
  411. # as well as the repo URL
  412. local remoteRepo="${2}"
  413. # A local variable containing the message to be displayed
  414. local str="Check for existing repository in ${1}"
  415. # Show the message
  416. echo -ne " ${INFO} ${str}..."
  417. # Check if the directory is a repository
  418. if is_repo "${directory}"; then
  419. # Show that we're checking it
  420. echo -e "${OVER} ${TICK} ${str}"
  421. # Update the repo, returning an error message on failure
  422. update_repo "${directory}" || { echo -e "\\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; }
  423. # If it's not a .git repo,
  424. else
  425. # Show an error
  426. echo -e "${OVER} ${CROSS} ${str}"
  427. # Attempt to make the repository, showing an error on failure
  428. make_repo "${directory}" "${remoteRepo}" || { echo -e "\\n ${COL_LIGHT_RED}Error: Could not update local repository. Contact support.${COL_NC}"; exit 1; }
  429. fi
  430. # echo a blank line
  431. echo ""
  432. # and return success?
  433. return 0
  434. }
  435.  
  436. # Reset a repo to get rid of any local changed
  437. resetRepo() {
  438. # Use named variables for arguments
  439. local directory="${1}"
  440. # Move into the directory
  441. cd "${directory}" &> /dev/null || return 1
  442. # Store the message in a variable
  443. str="Resetting repository within ${1}..."
  444. # Show the message
  445. echo -ne " ${INFO} ${str}"
  446. # Use git to remove the local changes
  447. git reset --hard &> /dev/null || return $?
  448. # And show the status
  449. echo -e "${OVER} ${TICK} ${str}"
  450. # Returning success anyway?
  451. return 0
  452. }
  453.  
  454. # We need to know the IPv4 information so we can effectively setup the DNS server
  455. # Without this information, we won't know where to Pi-hole will be found
  456. find_IPv4_information() {
  457. # Named, local variables
  458. local route
  459. # Find IP used to route to outside world by checking the the route to Google's public DNS server
  460. route=$(ip route get 8.8.8.8)
  461. # Use awk to strip out just the interface device as it is used in future commands
  462. IPv4dev=$(awk '{for (i=1; i<=NF; i++) if ($i~/dev/) print $(i+1)}' <<< "${route}")
  463. # Get just the IP address
  464. IPv4bare=$(awk '{print $7}' <<< "${route}")
  465. # Append the CIDR notation to the IP address
  466. IPV4_ADDRESS=$(ip -o -f inet addr show | grep "${IPv4bare}" | awk '{print $4}' | awk 'END {print}')
  467. # Get the default gateway (the way to reach the Internet)
  468. IPv4gw=$(awk '{print $3}' <<< "${route}")
  469. }
  470.  
  471. # Get available interfaces that are UP
  472. get_available_interfaces() {
  473. # There may be more than one so it's all stored in a variable
  474. availableInterfaces=$(ip --oneline link show up | grep -v "lo" | awk '{print $2}' | cut -d':' -f1 | cut -d'@' -f1)
  475. }
  476.  
  477. # A function for displaying the dialogs the user sees when first running the installer
  478. welcomeDialogs() {
  479. # Display the welcome dialog using an appropriately sized window via the calculation conducted earlier in the script
  480. whiptail --msgbox --backtitle "Welcome" --title "Pi-hole automated installer" "\\n\\nThis installer will transform your device into a network-wide ad blocker!" ${r} ${c}
  481.  
  482. # Request that users donate if they enjoy the software since we all work on it in our free time
  483. 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}
  484.  
  485. # Explain the need for a static address
  486. 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.
  487.  
  488. In the next section, you can choose to use your current network settings (DHCP) or to manually edit them." ${r} ${c}
  489. }
  490.  
  491. # We need to make sure there is enough space before installing, so there is a function to check this
  492. verifyFreeDiskSpace() {
  493. # 50MB is the minimum space needed (45MB install (includes web admin bootstrap/jquery libraries etc) + 5MB one day of logs.)
  494. # - Fourdee: Local ensures the variable is only created, and accessible within this function/void. Generally considered a "good" coding practice for non-global variables.
  495. local str="Disk space check"
  496. # Required space in KB
  497. local required_free_kilobytes=51200
  498. # Calculate existing free space on this machine
  499. local existing_free_kilobytes
  500. existing_free_kilobytes=$(df -Pk | grep -m1 '\/$' | awk '{print $4}')
  501.  
  502. # If the existing space is not an integer,
  503. if ! [[ "${existing_free_kilobytes}" =~ ^([0-9])+$ ]]; then
  504. # show an error that we can't determine the free space
  505. echo -e " ${CROSS} ${str}"
  506. echo -e " ${INFO} Unknown free disk space!"
  507. echo -e " ${INFO} We were unable to determine available free disk space on this system."
  508. echo -e " ${INFO} You may override this check, however, it is not recommended"
  509. echo -e " ${INFO} The option '${COL_LIGHT_RED}--i_do_not_follow_recommendations${COL_NC}' can override this"
  510. echo -e " ${INFO} e.g: curl -L https://install.pi-hole.net | bash /dev/stdin ${COL_LIGHT_RED}<option>${COL_NC}"
  511. # exit with an error code
  512. exit 1
  513. # If there is insufficient free disk space,
  514. elif [[ "${existing_free_kilobytes}" -lt "${required_free_kilobytes}" ]]; then
  515. # show an error message
  516. echo -e " ${CROSS} ${str}"
  517. echo -e " ${INFO} Your system disk appears to only have ${existing_free_kilobytes} KB free"
  518. echo -e " ${INFO} It is recommended to have a minimum of ${required_free_kilobytes} KB to run the Pi-hole"
  519. # if the vcgencmd command exists,
  520. if command -v vcgencmd &> /dev/null; then
  521. # it's probably a Raspbian install, so show a message about expanding the filesystem
  522. echo -e " ${INFO} If this is a new install you may need to expand your disk"
  523. echo -e " ${INFO} Run 'sudo raspi-config', and choose the 'expand file system' option"
  524. echo -e " ${INFO} After rebooting, run this installation again"
  525. echo -e " ${INFO} e.g: curl -L https://install.pi-hole.net | bash"
  526. fi
  527. # Show there is not enough free space
  528. echo -e "\\n ${COL_LIGHT_RED}Insufficient free space, exiting...${COL_NC}"
  529. # and exit with an error
  530. exit 1
  531. # Otherwise,
  532. else
  533. # Show that we're running a disk space check
  534. echo -e " ${TICK} ${str}"
  535. fi
  536. }
  537.  
  538. # A function that let's the user pick an interface to use with Pi-hole
  539. chooseInterface() {
  540. # Turn the available interfaces into an array so it can be used with a whiptail dialog
  541. local interfacesArray=()
  542. # Number of available interfaces
  543. local interfaceCount
  544. # Whiptail variable storage
  545. local chooseInterfaceCmd
  546. # Temporary Whiptail options storage
  547. local chooseInterfaceOptions
  548. # Loop sentinel variable
  549. local firstLoop=1
  550.  
  551. # Find out how many interfaces are available to choose from
  552. interfaceCount=$(echo "${availableInterfaces}" | wc -l)
  553.  
  554. # If there is one interface,
  555. if [[ "${interfaceCount}" -eq 1 ]]; then
  556. # Set it as the interface to use since there is no other option
  557. PIHOLE_INTERFACE="${availableInterfaces}"
  558. # Otherwise,
  559. else
  560. # While reading through the available interfaces
  561. while read -r line; do
  562. # use a variable to set the option as OFF to begin with
  563. mode="OFF"
  564. # If it's the first loop,
  565. if [[ "${firstLoop}" -eq 1 ]]; then
  566. # set this as the interface to use (ON)
  567. firstLoop=0
  568. mode="ON"
  569. fi
  570. # Put all these interfaces into an array
  571. interfacesArray+=("${line}" "available" "${mode}")
  572. # Feed the available interfaces into this while loop
  573. done <<< "${availableInterfaces}"
  574. # The whiptail command that will be run, stored in a variable
  575. chooseInterfaceCmd=(whiptail --separate-output --radiolist "Choose An Interface (press space to select)" ${r} ${c} ${interfaceCount})
  576. # Now run the command using the interfaces saved into the array
  577. chooseInterfaceOptions=$("${chooseInterfaceCmd[@]}" "${interfacesArray[@]}" 2>&1 >/dev/tty) || \
  578. # If the user chooses Cancel, exit
  579. { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  580. # For each interface
  581. for desiredInterface in ${chooseInterfaceOptions}; do
  582. # Set the one the user selected as the interface to use
  583. PIHOLE_INTERFACE=${desiredInterface}
  584. # and show this information to the user
  585. echo -e " ${INFO} Using interface: $PIHOLE_INTERFACE"
  586. done
  587. fi
  588. }
  589.  
  590. # This lets us prefer ULA addresses over GUA
  591. # This caused problems for some users when their ISP changed their IPv6 addresses
  592. # See https://github.com/pi-hole/pi-hole/issues/1473#issuecomment-301745953
  593. testIPv6() {
  594. # first will contain fda2 (ULA)
  595. first="$(cut -f1 -d":" <<< "$1")"
  596. # value1 will contain 253 which is the decimal value corresponding to 0xfd
  597. value1=$(( (0x$first)/256 ))
  598. # will contain 162 which is the decimal value corresponding to 0xa2
  599. value2=$(( (0x$first)%256 ))
  600. # the ULA test is testing for fc00::/7 according to RFC 4193
  601. if (( (value1&254)==252 )); then
  602. echo "ULA"
  603. fi
  604. # the GUA test is testing for 2000::/3 according to RFC 4291
  605. if (( (value1&112)==32 )); then
  606. echo "GUA"
  607. fi
  608. # the LL test is testing for fe80::/10 according to RFC 4193
  609. if (( (value1)==254 )) && (( (value2&192)==128 )); then
  610. echo "Link-local"
  611. fi
  612. }
  613.  
  614. # A dialog for showing the user about IPv6 blocking
  615. useIPv6dialog() {
  616. # Determine the IPv6 address used for blocking
  617. IPV6_ADDRESSES=($(ip -6 address | grep 'scope global' | awk '{print $2}'))
  618.  
  619. # For each address in the array above, determine the type of IPv6 address it is
  620. for i in "${IPV6_ADDRESSES[@]}"; do
  621. # Check if it's ULA, GUA, or LL by using the function created earlier
  622. result=$(testIPv6 "$i")
  623. # If it's a ULA address, use it and store it as a global variable
  624. [[ "${result}" == "ULA" ]] && ULA_ADDRESS="${i%/*}"
  625. # If it's a GUA address, we can still use it si store it as a global variable
  626. [[ "${result}" == "GUA" ]] && GUA_ADDRESS="${i%/*}"
  627. done
  628.  
  629. # Determine which address to be used: Prefer ULA over GUA or don't use any if none found
  630. # If the ULA_ADDRESS contains a value,
  631. if [[ ! -z "${ULA_ADDRESS}" ]]; then
  632. # set the IPv6 address to the ULA address
  633. IPV6_ADDRESS="${ULA_ADDRESS}"
  634. # Show this info to the user
  635. echo -e " ${INFO} Found IPv6 ULA address, using it for blocking IPv6 ads"
  636. # Otherwise, if the GUA_ADDRESS has a value,
  637. elif [[ ! -z "${GUA_ADDRESS}" ]]; then
  638. # Let the user know
  639. echo -e " ${INFO} Found IPv6 GUA address, using it for blocking IPv6 ads"
  640. # And assign it to the global variable
  641. IPV6_ADDRESS="${GUA_ADDRESS}"
  642. # If none of those work,
  643. else
  644. # explain that IPv6 blocking will not be used
  645. echo -e " ${INFO} Unable to find IPv6 ULA/GUA address, IPv6 adblocking will not be enabled"
  646. # So set the variable to be empty
  647. IPV6_ADDRESS=""
  648. fi
  649.  
  650. # If the IPV6_ADDRESS contains a value
  651. if [[ ! -z "${IPV6_ADDRESS}" ]]; then
  652. # Display that IPv6 is supported and will be used
  653. whiptail --msgbox --backtitle "IPv6..." --title "IPv6 Supported" "$IPV6_ADDRESS will be used to block ads." ${r} ${c}
  654. fi
  655. }
  656.  
  657. # A function to check if we should use IPv4 and/or IPv6 for blocking ads
  658. use4andor6() {
  659. # Named local variables
  660. local useIPv4
  661. local useIPv6
  662. # Let use select IPv4 and/or IPv6 via a checklist
  663. cmd=(whiptail --separate-output --checklist "Select Protocols (press space to select)" ${r} ${c} 2)
  664. # In an array, show the options available:
  665. # IPv4 (on by default)
  666. options=(IPv4 "Block ads over IPv4" on
  667. # or IPv6 (on by default if available)
  668. IPv6 "Block ads over IPv6" on)
  669. # In a variable, show the choices available; exit if Cancel is selected
  670. choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  671. # For each choice available,
  672. for choice in ${choices}
  673. do
  674. # Set the values to true
  675. case ${choice} in
  676. IPv4 ) useIPv4=true;;
  677. IPv6 ) useIPv6=true;;
  678. esac
  679. done
  680. # If IPv4 is to be used,
  681. if [[ "${useIPv4}" ]]; then
  682. # Run our function to get the information we need
  683. find_IPv4_information
  684. getStaticIPv4Settings
  685. setStaticIPv4
  686. fi
  687. # If IPv6 is to be used,
  688. if [[ "${useIPv6}" ]]; then
  689. # Run our function to get this information
  690. useIPv6dialog
  691. fi
  692. # Echo the information to the user
  693. echo -e " ${INFO} IPv4 address: ${IPV4_ADDRESS}"
  694. echo -e " ${INFO} IPv6 address: ${IPV6_ADDRESS}"
  695. # If neither protocol is selected,
  696. if [[ ! "${useIPv4}" ]] && [[ ! "${useIPv6}" ]]; then
  697. # Show an error in red
  698. echo -e " ${COL_LIGHT_RED}Error: Neither IPv4 or IPv6 selected${COL_NC}"
  699. # and exit with an error
  700. exit 1
  701. fi
  702. }
  703.  
  704. #
  705. getStaticIPv4Settings() {
  706. # Local, named variables
  707. local ipSettingsCorrect
  708. # Ask if the user wants to use DHCP settings as their static IP
  709. # This is useful for users that are using DHCP reservations; then we can just use the information gathered via our functions
  710. if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Do you want to use your current network settings as a static address?
  711. IP address: ${IPV4_ADDRESS}
  712. Gateway: ${IPv4gw}" ${r} ${c}; then
  713. # If they choose yes, let the user know that the IP address will not be available via DHCP and may cause a conflict.
  714. whiptail --msgbox --backtitle "IP information" --title "FYI: IP Conflict" "It is possible your router could still try to assign this IP to a device, which would cause a conflict. But in most cases the router is smart enough to not do that.
  715. If you are worried, either manually set the address, or modify the DHCP reservation pool so it does not include the IP you want.
  716. It is also possible to use a DHCP reservation, but if you are going to do that, you might as well set a static address." ${r} ${c}
  717. # Nothing else to do since the variables are already set above
  718. else
  719. # Otherwise, we need to ask the user to input their desired settings.
  720. # Start by getting the IPv4 address (pre-filling it with info gathered from DHCP)
  721. # Start a loop to let the user enter their information with the chance to go back and edit it if necessary
  722. until [[ "${ipSettingsCorrect}" = True ]]; do
  723.  
  724. # Ask for the IPv4 address
  725. 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) || \
  726. # Cancelling IPv4 settings window
  727. { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  728. echo -e " ${INFO} Your static IPv4 address: ${IPV4_ADDRESS}"
  729.  
  730. # Ask for the gateway
  731. IPv4gw=$(whiptail --backtitle "Calibrating network interface" --title "IPv4 gateway (router)" --inputbox "Enter your desired IPv4 default gateway" ${r} ${c} "${IPv4gw}" 3>&1 1>&2 2>&3) || \
  732. # Cancelling gateway settings window
  733. { ipSettingsCorrect=False; echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  734. echo -e " ${INFO} Your static IPv4 gateway: ${IPv4gw}"
  735.  
  736. # Give the user a chance to review their settings before moving on
  737. if whiptail --backtitle "Calibrating network interface" --title "Static IP Address" --yesno "Are these settings correct?
  738. IP address: ${IPV4_ADDRESS}
  739. Gateway: ${IPv4gw}" ${r} ${c}; then
  740. # After that's done, the loop ends and we move on
  741. ipSettingsCorrect=True
  742. else
  743. # If the settings are wrong, the loop continues
  744. ipSettingsCorrect=False
  745. fi
  746. done
  747. # End the if statement for DHCP vs. static
  748. fi
  749. }
  750.  
  751. # dhcpcd is very annoying,
  752. setDHCPCD() {
  753. # but we can append these lines to dhcpcd.conf to enable a static IP
  754. echo "interface ${PIHOLE_INTERFACE}
  755. static ip_address=${IPV4_ADDRESS}
  756. static routers=${IPv4gw}
  757. static domain_name_servers=127.0.0.1" | tee -a /etc/dhcpcd.conf >/dev/null
  758. }
  759.  
  760. setStaticIPv4() {
  761. # Local, named variables
  762. local IFCFG_FILE
  763. local IPADDR
  764. local CIDR
  765. # For the Debian family, if dhcpcd.conf exists,
  766. if [[ -f "/etc/dhcpcd.conf" ]]; then
  767. # check if the IP is already in the file
  768. if grep -q "${IPV4_ADDRESS}" /etc/dhcpcd.conf; then
  769. echo -e " ${INFO} Static IP already configured"
  770. # If it's not,
  771. else
  772. # set it using our function
  773. setDHCPCD
  774. # Then use the ip command to immediately set the new address
  775. ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}"
  776. # Also give a warning that the user may need to reboot their system
  777. echo -e " ${TICK} Set IP address to ${IPV4_ADDRESS%/*}
  778. You may need to restart after the install is complete"
  779. fi
  780. # If it's not Debian, check if it's the Fedora family by checking for the file below
  781. elif [[ -f "/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}" ]];then
  782. # If it exists,
  783. IFCFG_FILE=/etc/sysconfig/network-scripts/ifcfg-${PIHOLE_INTERFACE}
  784. IPADDR=$(echo "${IPV4_ADDRESS}" | cut -f1 -d/)
  785. # check if the desired IP is already set
  786. if grep -Eq "${IPADDR}(\\b|\\/)" "${IFCFG_FILE}"; then
  787. echo -e " ${INFO} Static IP already configured"
  788. # Otherwise,
  789. else
  790. # Put the IP in variables without the CIDR notation
  791. CIDR=$(echo "${IPV4_ADDRESS}" | cut -f2 -d/)
  792. # Backup existing interface configuration:
  793. cp "${IFCFG_FILE}" "${IFCFG_FILE}".pihole.orig
  794. # Build Interface configuration file using the GLOBAL variables we have
  795. {
  796. echo "# Configured via Pi-hole installer"
  797. echo "DEVICE=$PIHOLE_INTERFACE"
  798. echo "BOOTPROTO=none"
  799. echo "ONBOOT=yes"
  800. echo "IPADDR=$IPADDR"
  801. echo "PREFIX=$CIDR"
  802. echo "GATEWAY=$IPv4gw"
  803. echo "DNS1=$PIHOLE_DNS_1"
  804. echo "DNS2=$PIHOLE_DNS_2"
  805. echo "USERCTL=no"
  806. }> "${IFCFG_FILE}"
  807. # Use ip to immediately set the new address
  808. ip addr replace dev "${PIHOLE_INTERFACE}" "${IPV4_ADDRESS}"
  809. # If NetworkMangler command line interface exists and ready to mangle,
  810. if command -v nmcli &> /dev/null && nmcli general status &> /dev/null; then
  811. # Tell NetworkManagler to read our new sysconfig file
  812. nmcli con load "${IFCFG_FILE}" > /dev/null
  813. fi
  814. # Show a warning that the user may need to restart
  815. echo -e " ${TICK} Set IP address to ${IPV4_ADDRESS%/*}
  816. You may need to restart after the install is complete"
  817. fi
  818. # If all that fails,
  819. else
  820. # show an error and exit
  821. echo -e " ${INFO} Warning: Unable to locate configuration file to set static IPv4 address"
  822. exit 1
  823. fi
  824. }
  825.  
  826. # Check an IP address to see if it is a valid one
  827. valid_ip() {
  828. # Local, named variables
  829. local ip=${1}
  830. local stat=1
  831.  
  832. # If the IP matches the format xxx.xxx.xxx.xxx,
  833. if [[ "${ip}" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
  834. # Save the old Internal Field Separator in a variable
  835. OIFS=$IFS
  836. # and set the new one to a dot (period)
  837. IFS='.'
  838. # Put the IP into an array
  839. ip=(${ip})
  840. # Restore the IFS to what it was
  841. IFS=${OIFS}
  842. ## Evaluate each octet by checking if it's less than or equal to 255 (the max for each octet)
  843. [[ "${ip[0]}" -le 255 && "${ip[1]}" -le 255 \
  844. && "${ip[2]}" -le 255 && "${ip[3]}" -le 255 ]]
  845. # Save the exit code
  846. stat=$?
  847. fi
  848. # Return the exit code
  849. return ${stat}
  850. }
  851.  
  852. # A function to choose the upstream DNS provider(s)
  853. setDNS() {
  854. # Local, named variables
  855. local DNSSettingsCorrect
  856.  
  857. # In an array, list the available upstream providers
  858. DNSChooseOptions=(Google ""
  859. OpenDNS ""
  860. Level3 ""
  861. Norton ""
  862. Comodo ""
  863. DNSWatch ""
  864. Quad9 ""
  865. FamilyShield ""
  866. Cloudflare ""
  867. Custom "")
  868. # In a whiptail dialog, show the options
  869. DNSchoices=$(whiptail --separate-output --menu "Select Upstream DNS Provider. To use your own, select Custom." ${r} ${c} 7 \
  870. "${DNSChooseOptions[@]}" 2>&1 >/dev/tty) || \
  871. # exit if Cancel is selected
  872. { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  873.  
  874. # Display the selection
  875. echo -ne " ${INFO} Using "
  876. # Depending on the user's choice, set the GLOBAl variables to the IP of the respective provider
  877. case ${DNSchoices} in
  878. Google)
  879. echo "Google DNS servers"
  880. PIHOLE_DNS_1="8.8.8.8"
  881. PIHOLE_DNS_2="8.8.4.4"
  882. ;;
  883. OpenDNS)
  884. echo "OpenDNS servers"
  885. PIHOLE_DNS_1="208.67.222.222"
  886. PIHOLE_DNS_2="208.67.220.220"
  887. ;;
  888. Level3)
  889. echo "Level3 servers"
  890. PIHOLE_DNS_1="4.2.2.1"
  891. PIHOLE_DNS_2="4.2.2.2"
  892. ;;
  893. Norton)
  894. echo "Norton ConnectSafe servers"
  895. PIHOLE_DNS_1="199.85.126.10"
  896. PIHOLE_DNS_2="199.85.127.10"
  897. ;;
  898. Comodo)
  899. echo "Comodo Secure servers"
  900. PIHOLE_DNS_1="8.26.56.26"
  901. PIHOLE_DNS_2="8.20.247.20"
  902. ;;
  903. DNSWatch)
  904. echo "DNS.WATCH servers"
  905. PIHOLE_DNS_1="84.200.69.80"
  906. PIHOLE_DNS_2="84.200.70.40"
  907. ;;
  908. Quad9)
  909. echo "Quad9 servers"
  910. PIHOLE_DNS_1="9.9.9.9"
  911. PIHOLE_DNS_2="149.112.112.112"
  912. ;;
  913. FamilyShield)
  914. echo "FamilyShield servers"
  915. PIHOLE_DNS_1="208.67.222.123"
  916. PIHOLE_DNS_2="208.67.220.123"
  917. ;;
  918. Cloudflare)
  919. echo "Cloudflare servers"
  920. PIHOLE_DNS_1="1.1.1.1"
  921. PIHOLE_DNS_2="1.0.0.1"
  922. ;;
  923. Custom)
  924. # Until the DNS settings are selected,
  925. until [[ "${DNSSettingsCorrect}" = True ]]; do
  926. #
  927. strInvalid="Invalid"
  928. # If the first
  929. if [[ ! "${PIHOLE_DNS_1}" ]]; then
  930. # and second upstream servers do not exist
  931. if [[ ! "${PIHOLE_DNS_2}" ]]; then
  932. prePopulate=""
  933. # Otherwise,
  934. else
  935. prePopulate=", ${PIHOLE_DNS_2}"
  936. fi
  937. elif [[ "${PIHOLE_DNS_1}" ]] && [[ ! "${PIHOLE_DNS_2}" ]]; then
  938. prePopulate="${PIHOLE_DNS_1}"
  939. elif [[ "${PIHOLE_DNS_1}" ]] && [[ "${PIHOLE_DNS_2}" ]]; then
  940. prePopulate="${PIHOLE_DNS_1}, ${PIHOLE_DNS_2}"
  941. fi
  942.  
  943. # Dialog for the user to enter custom upstream servers
  944. piholeDNS=$(whiptail --backtitle "Specify Upstream DNS Provider(s)" --inputbox "Enter your desired upstream DNS provider(s), separated by a comma.\\n\\nFor example '8.8.8.8, 8.8.4.4'" ${r} ${c} "${prePopulate}" 3>&1 1>&2 2>&3) || \
  945. { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  946. #
  947. PIHOLE_DNS_1=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$1}')
  948. PIHOLE_DNS_2=$(echo "${piholeDNS}" | sed 's/[, \t]\+/,/g' | awk -F, '{print$2}')
  949. # If the IP is valid,
  950. if ! valid_ip "${PIHOLE_DNS_1}" || [[ ! "${PIHOLE_DNS_1}" ]]; then
  951. # store it in the variable so we can use it
  952. PIHOLE_DNS_1=${strInvalid}
  953. fi
  954. # Do the same for the secondary server
  955. if ! valid_ip "${PIHOLE_DNS_2}" && [[ "${PIHOLE_DNS_2}" ]]; then
  956. PIHOLE_DNS_2=${strInvalid}
  957. fi
  958. # If either of the DNS servers are invalid,
  959. if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]] || [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then
  960. # explain this to the user
  961. 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}
  962. # and set the variables back to nothing
  963. if [[ "${PIHOLE_DNS_1}" == "${strInvalid}" ]]; then
  964. PIHOLE_DNS_1=""
  965. fi
  966. if [[ "${PIHOLE_DNS_2}" == "${strInvalid}" ]]; then
  967. PIHOLE_DNS_2=""
  968. fi
  969. # Since the settings will not work, stay in the loop
  970. DNSSettingsCorrect=False
  971. # Otherwise,
  972. else
  973. # Show the settings
  974. 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
  975. # and break from the loop since the servers are valid
  976. DNSSettingsCorrect=True
  977. # Otherwise,
  978. else
  979. # If the settings are wrong, the loop continues
  980. DNSSettingsCorrect=False
  981. fi
  982. fi
  983. done
  984. ;;
  985. esac
  986. }
  987.  
  988. # Allow the user to enable/disable logging
  989. setLogging() {
  990. # Local, named variables
  991. local LogToggleCommand
  992. local LogChooseOptions
  993. local LogChoices
  994.  
  995. # Ask if the user wants to log queries
  996. LogToggleCommand=(whiptail --separate-output --radiolist "Do you want to log queries?" "${r}" "${c}" 6)
  997. # The default selection is on
  998. LogChooseOptions=("On (Recommended)" "" on
  999. Off "" off)
  1000. # Get the user's choice
  1001. LogChoices=$("${LogToggleCommand[@]}" "${LogChooseOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1)
  1002. case ${LogChoices} in
  1003. # If it's on
  1004. "On (Recommended)")
  1005. echo -e " ${INFO} Logging On."
  1006. # Set the GLOBAL variable to true so we know what they selected
  1007. QUERY_LOGGING=true
  1008. ;;
  1009. # Otherwise, it's off,
  1010. Off)
  1011. echo -e " ${INFO} Logging Off."
  1012. # So set it to false
  1013. QUERY_LOGGING=false
  1014. ;;
  1015. esac
  1016. }
  1017.  
  1018. # Function to ask the user if they want to install the dashboard
  1019. setAdminFlag() {
  1020. # Local, named variables
  1021. local WebToggleCommand
  1022. local WebChooseOptions
  1023. local WebChoices
  1024.  
  1025. # Similar to the logging function, ask what the user wants
  1026. WebToggleCommand=(whiptail --separate-output --radiolist "Do you wish to install the web admin interface?" ${r} ${c} 6)
  1027. # with the default being enabled
  1028. WebChooseOptions=("On (Recommended)" "" on
  1029. Off "" off)
  1030. WebChoices=$("${WebToggleCommand[@]}" "${WebChooseOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1)
  1031. # Depending on their choice
  1032. case ${WebChoices} in
  1033. "On (Recommended)")
  1034. echo -e " ${INFO} Web Interface On"
  1035. # Set it to true
  1036. INSTALL_WEB_INTERFACE=true
  1037. ;;
  1038. Off)
  1039. echo -e " ${INFO} Web Interface Off"
  1040. # or false
  1041. INSTALL_WEB_INTERFACE=false
  1042. ;;
  1043. esac
  1044.  
  1045. # Request user to install web server, if --disable-install-webserver has not been used (INSTALL_WEB_SERVER=true is default).
  1046. if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
  1047. WebToggleCommand=(whiptail --separate-output --radiolist "Do you wish to install the web server (lighttpd)?\\n\\nNB: If you disable this, and, do not have an existing webserver installed, the web interface will not function." "${r}" "${c}" 6)
  1048. # with the default being enabled
  1049. WebChooseOptions=("On (Recommended)" "" on
  1050. Off "" off)
  1051. WebChoices=$("${WebToggleCommand[@]}" "${WebChooseOptions[@]}" 2>&1 >/dev/tty) || (echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}" && exit 1)
  1052. # Depending on their choice
  1053. case ${WebChoices} in
  1054. "On (Recommended)")
  1055. echo -e " ${INFO} Web Server On"
  1056. # set it to true, as clearly seen below.
  1057. INSTALL_WEB_SERVER=true
  1058. ;;
  1059. Off)
  1060. echo -e " ${INFO} Web Server Off"
  1061. # or false
  1062. INSTALL_WEB_SERVER=false
  1063. ;;
  1064. esac
  1065. fi
  1066. }
  1067.  
  1068. # A function to display a list of example blocklists for users to select
  1069. chooseBlocklists() {
  1070. # Back up any existing adlist file, on the off chance that it exists. Useful in case of a reconfigure.
  1071. if [[ -f "${adlistFile}" ]]; then
  1072. mv "${adlistFile}" "${adlistFile}.old"
  1073. fi
  1074. # Let user select (or not) blocklists via a checklist
  1075. cmd=(whiptail --separate-output --checklist "Pi-hole relies on third party lists in order to block ads.\\n\\nYou can use the suggestions below, and/or add your own after installation\\n\\nTo deselect any list, use the arrow keys and spacebar" "${r}" "${c}" 7)
  1076. # In an array, show the options available (all off by default):
  1077. options=(StevenBlack "StevenBlack's Unified Hosts List" on
  1078. MalwareDom "MalwareDomains" on
  1079. Cameleon "Cameleon" on
  1080. ZeusTracker "ZeusTracker" on
  1081. DisconTrack "Disconnect.me Tracking" on
  1082. DisconAd "Disconnect.me Ads" on
  1083. HostsFile "Hosts-file.net Ads" on)
  1084.  
  1085. # In a variable, show the choices available; exit if Cancel is selected
  1086. choices=$("${cmd[@]}" "${options[@]}" 2>&1 >/dev/tty) || { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; rm "${adlistFile}" ;exit 1; }
  1087. # For each choice available,
  1088. for choice in ${choices}
  1089. do
  1090. # Set the values to true
  1091. case ${choice} in
  1092. StevenBlack ) echo "https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts" >> "${adlistFile}";;
  1093. MalwareDom ) echo "https://mirror1.malwaredomains.com/files/justdomains" >> "${adlistFile}";;
  1094. Cameleon ) echo "http://sysctl.org/cameleon/hosts" >> "${adlistFile}";;
  1095. ZeusTracker ) echo "https://zeustracker.abuse.ch/blocklist.php?download=domainblocklist" >> "${adlistFile}";;
  1096. DisconTrack ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt" >> "${adlistFile}";;
  1097. DisconAd ) echo "https://s3.amazonaws.com/lists.disconnect.me/simple_ad.txt" >> "${adlistFile}";;
  1098. HostsFile ) echo "https://hosts-file.net/ad_servers.txt" >> "${adlistFile}";;
  1099. esac
  1100. done
  1101. }
  1102.  
  1103. # Check if /etc/dnsmasq.conf is from pi-hole. If so replace with an original and install new in .d directory
  1104. version_check_dnsmasq() {
  1105. # Local, named variables
  1106. local dnsmasq_conf="/etc/dnsmasq.conf"
  1107. local dnsmasq_conf_orig="/etc/dnsmasq.conf.orig"
  1108. local dnsmasq_pihole_id_string="addn-hosts=/etc/pihole/gravity.list"
  1109. local dnsmasq_original_config="${PI_HOLE_LOCAL_REPO}/advanced/dnsmasq.conf.original"
  1110. local dnsmasq_pihole_01_snippet="${PI_HOLE_LOCAL_REPO}/advanced/01-pihole.conf"
  1111. local dnsmasq_pihole_01_location="/etc/dnsmasq.d/01-pihole.conf"
  1112.  
  1113. # If the dnsmasq config file exists
  1114. if [[ -f "${dnsmasq_conf}" ]]; then
  1115. echo -ne " ${INFO} Existing dnsmasq.conf found..."
  1116. # If gravity.list is found within this file, we presume it's from older versions on Pi-hole,
  1117. if grep -q ${dnsmasq_pihole_id_string} ${dnsmasq_conf}; then
  1118. echo " it is from a previous Pi-hole install."
  1119. echo -ne " ${INFO} Backing up dnsmasq.conf to dnsmasq.conf.orig..."
  1120. # so backup the original file
  1121. mv -f ${dnsmasq_conf} ${dnsmasq_conf_orig}
  1122. echo -e "${OVER} ${TICK} Backing up dnsmasq.conf to dnsmasq.conf.orig..."
  1123. echo -ne " ${INFO} Restoring default dnsmasq.conf..."
  1124. # and replace it with the default
  1125. cp ${dnsmasq_original_config} ${dnsmasq_conf}
  1126. echo -e "${OVER} ${TICK} Restoring default dnsmasq.conf..."
  1127. # Otherwise,
  1128. else
  1129. # Don't to anything
  1130. echo " it is not a Pi-hole file, leaving alone!"
  1131. fi
  1132. else
  1133. # If a file cannot be found,
  1134. echo -ne " ${INFO} No dnsmasq.conf found... restoring default dnsmasq.conf..."
  1135. # restore the default one
  1136. cp ${dnsmasq_original_config} ${dnsmasq_conf}
  1137. echo -e "${OVER} ${TICK} No dnsmasq.conf found... restoring default dnsmasq.conf..."
  1138. fi
  1139.  
  1140. echo -en " ${INFO} Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf..."
  1141. # Check to see if dnsmasq directory exists (it may not due to being a fresh install and dnsmasq no longer being a dependency)
  1142. if [[ ! -d "/etc/dnsmasq.d" ]];then
  1143. mkdir "/etc/dnsmasq.d"
  1144. fi
  1145. # Copy the new Pi-hole DNS config file into the dnsmasq.d directory
  1146. cp ${dnsmasq_pihole_01_snippet} ${dnsmasq_pihole_01_location}
  1147. echo -e "${OVER} ${TICK} Copying 01-pihole.conf to /etc/dnsmasq.d/01-pihole.conf"
  1148. # Replace our placeholder values with the GLOBAL DNS variables that we populated earlier
  1149. # First, swap in the interface to listen on
  1150. sed -i "s/@INT@/$PIHOLE_INTERFACE/" ${dnsmasq_pihole_01_location}
  1151. if [[ "${PIHOLE_DNS_1}" != "" ]]; then
  1152. # Then swap in the primary DNS server
  1153. sed -i "s/@DNS1@/$PIHOLE_DNS_1/" ${dnsmasq_pihole_01_location}
  1154. else
  1155. #
  1156. sed -i '/^server=@DNS1@/d' ${dnsmasq_pihole_01_location}
  1157. fi
  1158. if [[ "${PIHOLE_DNS_2}" != "" ]]; then
  1159. # Then swap in the primary DNS server
  1160. sed -i "s/@DNS2@/$PIHOLE_DNS_2/" ${dnsmasq_pihole_01_location}
  1161. else
  1162. #
  1163. sed -i '/^server=@DNS2@/d' ${dnsmasq_pihole_01_location}
  1164. fi
  1165.  
  1166. #
  1167. sed -i 's/^#conf-dir=\/etc\/dnsmasq.d$/conf-dir=\/etc\/dnsmasq.d/' ${dnsmasq_conf}
  1168.  
  1169. # If the user does not want to enable logging,
  1170. if [[ "${QUERY_LOGGING}" == false ]] ; then
  1171. # Disable it by commenting out the directive in the DNS config file
  1172. sed -i 's/^log-queries/#log-queries/' ${dnsmasq_pihole_01_location}
  1173. # Otherwise,
  1174. else
  1175. # enable it by uncommenting the directive in the DNS config file
  1176. sed -i 's/^#log-queries/log-queries/' ${dnsmasq_pihole_01_location}
  1177. fi
  1178. }
  1179.  
  1180. # Clean an existing installation to prepare for upgrade/reinstall
  1181. clean_existing() {
  1182. # Local, named variables
  1183. # ${1} Directory to clean
  1184. local clean_directory="${1}"
  1185. # Make ${2} the new one?
  1186. shift
  1187. # ${2} Array of files to remove
  1188. local old_files=( "$@" )
  1189.  
  1190. # For each script found in the old files array
  1191. for script in "${old_files[@]}"; do
  1192. # Remove them
  1193. rm -f "${clean_directory}/${script}.sh"
  1194. done
  1195. }
  1196.  
  1197. # Install the scripts from repository to their various locations
  1198. installScripts() {
  1199. # Local, named variables
  1200. local str="Installing scripts from ${PI_HOLE_LOCAL_REPO}"
  1201. echo -ne " ${INFO} ${str}..."
  1202.  
  1203. # Clear out script files from Pi-hole scripts directory.
  1204. clean_existing "${PI_HOLE_INSTALL_DIR}" "${PI_HOLE_FILES[@]}"
  1205.  
  1206. # Install files from local core repository
  1207. if is_repo "${PI_HOLE_LOCAL_REPO}"; then
  1208. # move into the directory
  1209. cd "${PI_HOLE_LOCAL_REPO}"
  1210. # Install the scripts by:
  1211. # -o setting the owner to the user
  1212. # -Dm755 create all leading components of destination except the last, then copy the source to the destination and setting the permissions to 755
  1213. #
  1214. # This first one is the directory
  1215. install -o "${USER}" -Dm755 -d "${PI_HOLE_INSTALL_DIR}"
  1216. # The rest are the scripts Pi-hole needs
  1217. install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" gravity.sh
  1218. install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./advanced/Scripts/*.sh
  1219. install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./automated\ install/uninstall.sh
  1220. install -o "${USER}" -Dm755 -t "${PI_HOLE_INSTALL_DIR}" ./advanced/Scripts/COL_TABLE
  1221. install -o "${USER}" -Dm755 -t /usr/local/bin/ pihole
  1222. install -Dm644 ./advanced/bash-completion/pihole /etc/bash_completion.d/pihole
  1223. echo -e "${OVER} ${TICK} ${str}"
  1224.  
  1225. # Otherwise,
  1226. else
  1227. # Show an error and exit
  1228. echo -e "${OVER} ${CROSS} ${str}
  1229. ${COL_LIGHT_RED}Error: Local repo ${PI_HOLE_LOCAL_REPO} not found, exiting installer${COL_NC}"
  1230. return 1
  1231. fi
  1232. }
  1233.  
  1234. # Install the configs from PI_HOLE_LOCAL_REPO to their various locations
  1235. installConfigs() {
  1236. echo ""
  1237. echo -e " ${INFO} Installing configs from ${PI_HOLE_LOCAL_REPO}..."
  1238. # Make sure Pi-hole's config files are in place
  1239. version_check_dnsmasq
  1240. # Install empty file if it does not exist
  1241. if [[ ! -f "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" ]]; then
  1242. if ! install -o pihole -g pihole -m 664 /dev/null "${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf" &>/dev/nul; then
  1243. echo -e " ${COL_LIGHT_RED}Error: Unable to initialize configuration file ${PI_HOLE_CONFIG_DIR}/pihole-FTL.conf"
  1244. return 1
  1245. fi
  1246. fi
  1247. # Install an empty regex file
  1248. if [[ ! -f "${regexFile}" ]]; then
  1249. # Let PHP edit the regex file, if installed
  1250. install -o pihole -g "${LIGHTTPD_GROUP:-pihole}" -m 664 /dev/null "${regexFile}"
  1251. fi
  1252. # If the user chose to install the dashboard,
  1253. if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
  1254. # and if the Web server conf directory does not exist,
  1255. if [[ ! -d "/etc/lighttpd" ]]; then
  1256. # make it
  1257. mkdir /etc/lighttpd
  1258. # and set the owners
  1259. chown "${USER}":root /etc/lighttpd
  1260. # Otherwise, if the config file already exists
  1261. elif [[ -f "/etc/lighttpd/lighttpd.conf" ]]; then
  1262. # back up the original
  1263. mv /etc/lighttpd/lighttpd.conf /etc/lighttpd/lighttpd.conf.orig
  1264. fi
  1265. # and copy in the config file Pi-hole needs
  1266. cp ${PI_HOLE_LOCAL_REPO}/advanced/${LIGHTTPD_CFG} /etc/lighttpd/lighttpd.conf
  1267. # if there is a custom block page in the html/pihole directory, replace 404 handler in lighttpd config
  1268. if [[ -f "/var/www/html/pihole/custom.php" ]]; then
  1269. sed -i 's/^\(server\.error-handler-404\s*=\s*\).*$/\1"pihole\/custom\.php"/' /etc/lighttpd/lighttpd.conf
  1270. fi
  1271. # Make the directories if they do not exist and set the owners
  1272. mkdir -p /var/run/lighttpd
  1273. chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/run/lighttpd
  1274. mkdir -p /var/cache/lighttpd/compress
  1275. chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/compress
  1276. mkdir -p /var/cache/lighttpd/uploads
  1277. chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/cache/lighttpd/uploads
  1278. fi
  1279. }
  1280.  
  1281. install_manpage() {
  1282. # Copy Pi-hole man pages and call mandb to update man page database
  1283. # Default location for man files for /usr/local/bin is /usr/local/share/man
  1284. # on lightweight systems may not be present, so check before copying.
  1285. echo -en " ${INFO} Testing man page installation"
  1286. if ! command -v mandb &>/dev/null; then
  1287. # if mandb is not present, no manpage support
  1288. echo -e "${OVER} ${INFO} man not installed"
  1289. return
  1290. elif [[ ! -d "/usr/local/share/man" ]]; then
  1291. # appropriate directory for Pi-hole's man page is not present
  1292. echo -e "${OVER} ${INFO} man pages not installed"
  1293. return
  1294. fi
  1295. if [[ ! -d "/usr/local/share/man/man8" ]]; then
  1296. # if not present, create man8 directory
  1297. mkdir /usr/local/share/man/man8
  1298. fi
  1299. if [[ ! -d "/usr/local/share/man/man5" ]]; then
  1300. # if not present, create man8 directory
  1301. mkdir /usr/local/share/man/man5
  1302. fi
  1303. # Testing complete, copy the files & update the man db
  1304. cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole.8 /usr/local/share/man/man8/pihole.8
  1305. cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.8 /usr/local/share/man/man8/pihole-FTL.8
  1306. cp ${PI_HOLE_LOCAL_REPO}/manpages/pihole-FTL.conf.5 /usr/local/share/man/man5/pihole-FTL.conf.5
  1307. if mandb -q &>/dev/null; then
  1308. # Updated successfully
  1309. echo -e "${OVER} ${TICK} man pages installed and database updated"
  1310. return
  1311. else
  1312. # Something is wrong with the system's man installation, clean up
  1313. # our files, (leave everything how we found it).
  1314. rm /usr/local/share/man/man8/pihole.8 /usr/local/share/man/man8/pihole-FTL.8 /usr/local/share/man/man5/pihole-FTL.conf.5
  1315. echo -e "${OVER} ${CROSS} man page db not updated, man pages not installed"
  1316. fi
  1317. }
  1318.  
  1319. stop_service() {
  1320. # Stop service passed in as argument.
  1321. # Can softfail, as process may not be installed when this is called
  1322. local str="Stopping ${1} service"
  1323. echo -ne " ${INFO} ${str}..."
  1324. if command -v systemctl &> /dev/null; then
  1325. systemctl stop "${1}" &> /dev/null || true
  1326. else
  1327. service "${1}" stop &> /dev/null || true
  1328. fi
  1329. echo -e "${OVER} ${TICK} ${str}..."
  1330. }
  1331.  
  1332. # Start/Restart service passed in as argument
  1333. start_service() {
  1334. # Local, named variables
  1335. local str="Starting ${1} service"
  1336. echo -ne " ${INFO} ${str}..."
  1337. # If systemctl exists,
  1338. if command -v systemctl &> /dev/null; then
  1339. # use that to restart the service
  1340. systemctl restart "${1}" &> /dev/null
  1341. # Otherwise,
  1342. else
  1343. # fall back to the service command
  1344. service "${1}" restart &> /dev/null
  1345. fi
  1346. echo -e "${OVER} ${TICK} ${str}"
  1347. }
  1348.  
  1349. # Enable service so that it will start with next reboot
  1350. enable_service() {
  1351. # Local, named variables
  1352. local str="Enabling ${1} service to start on reboot"
  1353. echo -ne " ${INFO} ${str}..."
  1354. # If systemctl exists,
  1355. if command -v systemctl &> /dev/null; then
  1356. # use that to enable the service
  1357. systemctl enable "${1}" &> /dev/null
  1358. # Otherwise,
  1359. else
  1360. # use update-rc.d to accomplish this
  1361. update-rc.d "${1}" defaults &> /dev/null
  1362. fi
  1363. echo -e "${OVER} ${TICK} ${str}"
  1364. }
  1365.  
  1366. # Disable service so that it will not with next reboot
  1367. disable_service() {
  1368. # Local, named variables
  1369. local str="Disabling ${1} service"
  1370. echo -ne " ${INFO} ${str}..."
  1371. # If systemctl exists,
  1372. if command -v systemctl &> /dev/null; then
  1373. # use that to disable the service
  1374. systemctl disable "${1}" &> /dev/null
  1375. # Otherwise,
  1376. else
  1377. # use update-rc.d to accomplish this
  1378. update-rc.d "${1}" disable &> /dev/null
  1379. fi
  1380. echo -e "${OVER} ${TICK} ${str}"
  1381. }
  1382.  
  1383. check_service_active() {
  1384. # If systemctl exists,
  1385. if command -v systemctl &> /dev/null; then
  1386. # use that to check the status of the service
  1387. systemctl is-enabled "${1}" &> /dev/null
  1388. # Otherwise,
  1389. else
  1390. # fall back to service command
  1391. service "${1}" status &> /dev/null
  1392. fi
  1393. }
  1394.  
  1395. # Systemd-resolved's DNSStubListener and dnsmasq can't share port 53.
  1396. disable_resolved_stublistener() {
  1397. echo -en " ${INFO} Testing if systemd-resolved is enabled"
  1398. # Check if Systemd-resolved's DNSStubListener is enabled and active on port 53
  1399. if check_service_active "systemd-resolved"; then
  1400. # Check if DNSStubListener is enabled
  1401. echo -en " ${OVER} ${INFO} Testing if systemd-resolved DNSStub-Listener is active"
  1402. if ( grep -E '#?DNSStubListener=yes' /etc/systemd/resolved.conf &> /dev/null ); then
  1403. # Disable the DNSStubListener to unbind it from port 53
  1404. # Note that this breaks dns functionality on host until dnsmasq/ftl are up and running
  1405. echo -en "${OVER} ${TICK} Disabling systemd-resolved DNSStubListener"
  1406. # Make a backup of the original /etc/systemd/resolved.conf
  1407. # (This will need to be restored on uninstallation)
  1408. sed -r -i.orig 's/#?DNSStubListener=yes/DNSStubListener=no/g' /etc/systemd/resolved.conf
  1409. echo -e " and restarting systemd-resolved"
  1410. systemctl reload-or-restart systemd-resolved
  1411. else
  1412. echo -e "${OVER} ${INFO} Systemd-resolved does not need to be restarted"
  1413. fi
  1414. else
  1415. echo -e "${OVER} ${INFO} Systemd-resolved is not enabled"
  1416. fi
  1417. }
  1418.  
  1419. update_package_cache() {
  1420. # Running apt-get update/upgrade with minimal output can cause some issues with
  1421. # requiring user input (e.g password for phpmyadmin see #218)
  1422.  
  1423. # Update package cache on apt based OSes. Do this every time since
  1424. # it's quick and packages can be updated at any time.
  1425.  
  1426. # Local, named variables
  1427. local str="Update local cache of available packages"
  1428. echo ""
  1429. echo -ne " ${INFO} ${str}..."
  1430. # Create a command from the package cache variable
  1431. if eval "${UPDATE_PKG_CACHE}" &> /dev/null; then
  1432. echo -e "${OVER} ${TICK} ${str}"
  1433. # Otherwise,
  1434. else
  1435. # show an error and exit
  1436. echo -e "${OVER} ${CROSS} ${str}"
  1437. echo -ne " ${COL_LIGHT_RED}Error: Unable to update package cache. Please try \"${UPDATE_PKG_CACHE}\"${COL_NC}"
  1438. return 1
  1439. fi
  1440. }
  1441.  
  1442. # Let user know if they have outdated packages on their system and
  1443. # advise them to run a package update at soonest possible.
  1444. notify_package_updates_available() {
  1445. # Local, named variables
  1446. local str="Checking ${PKG_MANAGER} for upgraded packages"
  1447. echo -ne "\\n ${INFO} ${str}..."
  1448. # Store the list of packages in a variable
  1449. updatesToInstall=$(eval "${PKG_COUNT}")
  1450.  
  1451. if [[ -d "/lib/modules/$(uname -r)" ]]; then
  1452. if [[ "${updatesToInstall}" -eq 0 ]]; then
  1453. echo -e "${OVER} ${TICK} ${str}... up to date!"
  1454. echo ""
  1455. else
  1456. echo -e "${OVER} ${TICK} ${str}... ${updatesToInstall} updates available"
  1457. echo -e " ${INFO} ${COL_LIGHT_GREEN}It is recommended to update your OS after installing the Pi-hole! ${COL_NC}"
  1458. echo ""
  1459. fi
  1460. else
  1461. echo -e "${OVER} ${CROSS} ${str}
  1462. Kernel update detected. If the install fails, please reboot and try again\\n"
  1463. fi
  1464. }
  1465.  
  1466. # What's this doing outside of a function in the middle of nowhere?
  1467. counter=0
  1468.  
  1469. install_dependent_packages() {
  1470. # Local, named variables should be used here, especially for an iterator
  1471. # Add one to the counter
  1472. counter=$((counter+1))
  1473. # If it equals 1,
  1474. if [[ "${counter}" == 1 ]]; then
  1475. #
  1476. echo -e " ${INFO} Installer Dependency checks..."
  1477. else
  1478. #
  1479. echo -e " ${INFO} Main Dependency checks..."
  1480. fi
  1481.  
  1482. # Install packages passed in via argument array
  1483. # No spinner - conflicts with set -e
  1484. declare -a argArray1=("${!1}")
  1485. declare -a installArray
  1486.  
  1487. # Debian based package install - debconf will download the entire package list
  1488. # so we just create an array of packages not currently installed to cut down on the
  1489. # amount of download traffic.
  1490. # NOTE: We may be able to use this installArray in the future to create a list of package that were
  1491. # installed by us, and remove only the installed packages, and not the entire list.
  1492. if command -v debconf-apt-progress &> /dev/null; then
  1493. # For each package,
  1494. for i in "${argArray1[@]}"; do
  1495. echo -ne " ${INFO} Checking for $i..."
  1496. if dpkg-query -W -f='${Status}' "${i}" 2>/dev/null | grep "ok installed" &> /dev/null; then
  1497. echo -e "${OVER} ${TICK} Checking for $i"
  1498. else
  1499. echo -e "${OVER} ${INFO} Checking for $i (will be installed)"
  1500. installArray+=("${i}")
  1501. fi
  1502. done
  1503. if [[ "${#installArray[@]}" -gt 0 ]]; then
  1504. test_dpkg_lock
  1505. debconf-apt-progress -- "${PKG_INSTALL[@]}" "${installArray[@]}"
  1506. return
  1507. fi
  1508. echo ""
  1509. return 0
  1510. fi
  1511.  
  1512. # Install Fedora/CentOS packages
  1513. for i in "${argArray1[@]}"; do
  1514. echo -ne " ${INFO} Checking for $i..."
  1515. if ${PKG_MANAGER} -q list installed "${i}" &> /dev/null; then
  1516. echo -e "${OVER} ${TICK} Checking for $i"
  1517. else
  1518. echo -e "${OVER} ${INFO} Checking for $i (will be installed)"
  1519. installArray+=("${i}")
  1520. fi
  1521. done
  1522. if [[ "${#installArray[@]}" -gt 0 ]]; then
  1523. "${PKG_INSTALL[@]}" "${installArray[@]}" &> /dev/null
  1524. return
  1525. fi
  1526. echo ""
  1527. return 0
  1528. }
  1529.  
  1530. # Install the Web interface dashboard
  1531. installPiholeWeb() {
  1532. echo ""
  1533. echo " ${INFO} Installing blocking page..."
  1534.  
  1535. local str="Creating directory for blocking page, and copying files"
  1536. echo -ne " ${INFO} ${str}..."
  1537. # Install the directory
  1538. install -d /var/www/html/pihole
  1539. # and the blockpage
  1540. install -D ${PI_HOLE_LOCAL_REPO}/advanced/{index,blockingpage}.* /var/www/html/pihole/
  1541.  
  1542. # Remove superseded file
  1543. if [[ -e "/var/www/html/pihole/index.js" ]]; then
  1544. rm "/var/www/html/pihole/index.js"
  1545. fi
  1546.  
  1547. echo -e "${OVER} ${TICK} ${str}"
  1548.  
  1549. local str="Backing up index.lighttpd.html"
  1550. echo -ne " ${INFO} ${str}..."
  1551. # If the default index file exists,
  1552. if [[ -f "/var/www/html/index.lighttpd.html" ]]; then
  1553. # back it up
  1554. mv /var/www/html/index.lighttpd.html /var/www/html/index.lighttpd.orig
  1555. echo -e "${OVER} ${TICK} ${str}"
  1556. # Otherwise,
  1557. else
  1558. # don't do anything
  1559. echo -e "${OVER} ${CROSS} ${str}
  1560. No default index.lighttpd.html file found... not backing up"
  1561. fi
  1562.  
  1563. # Install Sudoers file
  1564. echo ""
  1565. local str="Installing sudoer file"
  1566. echo -ne " ${INFO} ${str}..."
  1567. # Make the .d directory if it doesn't exist
  1568. mkdir -p /etc/sudoers.d/
  1569. # and copy in the pihole sudoers file
  1570. cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole.sudo /etc/sudoers.d/pihole
  1571. # Add lighttpd user (OS dependent) to sudoers file
  1572. echo "${LIGHTTPD_USER} ALL=NOPASSWD: /usr/local/bin/pihole" >> /etc/sudoers.d/pihole
  1573.  
  1574. # If the Web server user is lighttpd,
  1575. if [[ "$LIGHTTPD_USER" == "lighttpd" ]]; then
  1576. # Allow executing pihole via sudo with Fedora
  1577. # Usually /usr/local/bin is not permitted as directory for sudoable programs
  1578. echo "Defaults secure_path = /sbin:/bin:/usr/sbin:/usr/bin:/usr/local/bin" >> /etc/sudoers.d/pihole
  1579. fi
  1580. # Set the strict permissions on the file
  1581. chmod 0440 /etc/sudoers.d/pihole
  1582. echo -e "${OVER} ${TICK} ${str}"
  1583. }
  1584.  
  1585. # Installs a cron file
  1586. installCron() {
  1587. # Install the cron job
  1588. local str="Installing latest Cron script"
  1589. echo ""
  1590. echo -ne " ${INFO} ${str}..."
  1591. # Copy the cron file over from the local repo
  1592. cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole.cron /etc/cron.d/pihole
  1593. # Randomize gravity update time
  1594. sed -i "s/59 1 /$((1 + RANDOM % 58)) $((3 + RANDOM % 2))/" /etc/cron.d/pihole
  1595. # Randomize update checker time
  1596. sed -i "s/59 17/$((1 + RANDOM % 58)) $((12 + RANDOM % 8))/" /etc/cron.d/pihole
  1597. echo -e "${OVER} ${TICK} ${str}"
  1598. }
  1599.  
  1600. # Gravity is a very important script as it aggregates all of the domains into a single HOSTS formatted list,
  1601. # which is what Pi-hole needs to begin blocking ads
  1602. runGravity() {
  1603. # Run gravity in the current shell
  1604. { /opt/pihole/gravity.sh --force; }
  1605. }
  1606.  
  1607. # Check if the pihole user exists and create if it does not
  1608. create_pihole_user() {
  1609. local str="Checking for user 'pihole'"
  1610. echo -ne " ${INFO} ${str}..."
  1611. # If the user pihole exists,
  1612. if id -u pihole &> /dev/null; then
  1613. # just show a success
  1614. echo -ne "${OVER} ${TICK} ${str}"
  1615. # Otherwise,
  1616. else
  1617. echo -ne "${OVER} ${CROSS} ${str}"
  1618. local str="Creating user 'pihole'"
  1619. echo -ne " ${INFO} ${str}..."
  1620. # create her with the useradd command
  1621. useradd -r -s /usr/sbin/nologin pihole
  1622. echo -ne "${OVER} ${TICK} ${str}"
  1623. fi
  1624. }
  1625.  
  1626. # Allow HTTP and DNS traffic
  1627. configureFirewall() {
  1628. echo ""
  1629. # If a firewall is running,
  1630. if firewall-cmd --state &> /dev/null; then
  1631. # ask if the user wants to install Pi-hole's default firewall rules
  1632. 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} || \
  1633. { echo -e " ${INFO} Not installing firewall rulesets."; return 0; }
  1634. echo -e " ${TICK} Configuring FirewallD for httpd and pihole-FTL"
  1635. # Allow HTTP and DNS traffic
  1636. firewall-cmd --permanent --add-service=http --add-service=dns
  1637. # Reload the firewall to apply these changes
  1638. firewall-cmd --reload
  1639. return 0
  1640. # Check for proper kernel modules to prevent failure
  1641. elif modinfo ip_tables &> /dev/null && command -v iptables &> /dev/null; then
  1642. # If chain Policy is not ACCEPT or last Rule is not ACCEPT
  1643. # then check and insert our Rules above the DROP/REJECT Rule.
  1644. if iptables -S INPUT | head -n1 | grep -qv '^-P.*ACCEPT$' || iptables -S INPUT | tail -n1 | grep -qv '^-\(A\|P\).*ACCEPT$'; then
  1645. 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} || \
  1646. { echo -e " ${INFO} Not installing firewall rulesets."; return 0; }
  1647. echo -e " ${TICK} Installing new IPTables firewall rulesets"
  1648. # Check chain first, otherwise a new rule will duplicate old ones
  1649. 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
  1650. 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
  1651. 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
  1652. 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
  1653. return 0
  1654. fi
  1655. # Otherwise,
  1656. else
  1657. # no firewall is running
  1658. echo -e " ${INFO} No active firewall detected.. skipping firewall configuration"
  1659. # so just exit
  1660. return 0
  1661. fi
  1662. echo -e " ${INFO} Skipping firewall configuration"
  1663. }
  1664.  
  1665. #
  1666. finalExports() {
  1667. # If the Web interface is not set to be installed,
  1668. if [[ "${INSTALL_WEB_INTERFACE}" == false ]]; then
  1669. # and if there is not an IPv4 address,
  1670. if [[ "${IPV4_ADDRESS}" ]]; then
  1671. # there is no block page, so set IPv4 to 0.0.0.0 (all IP addresses)
  1672. IPV4_ADDRESS="0.0.0.0"
  1673. fi
  1674. if [[ "${IPV6_ADDRESS}" ]]; then
  1675. # and IPv6 to ::/0
  1676. IPV6_ADDRESS="::/0"
  1677. fi
  1678. fi
  1679.  
  1680. # If the setup variable file exists,
  1681. if [[ -e "${setupVars}" ]]; then
  1682. # update the variables in the file
  1683. 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_SERVER/d;/INSTALL_WEB_INTERFACE/d;/LIGHTTPD_ENABLED/d;' "${setupVars}"
  1684. fi
  1685. # echo the information to the user
  1686. {
  1687. echo "PIHOLE_INTERFACE=${PIHOLE_INTERFACE}"
  1688. echo "IPV4_ADDRESS=${IPV4_ADDRESS}"
  1689. echo "IPV6_ADDRESS=${IPV6_ADDRESS}"
  1690. echo "PIHOLE_DNS_1=${PIHOLE_DNS_1}"
  1691. echo "PIHOLE_DNS_2=${PIHOLE_DNS_2}"
  1692. echo "QUERY_LOGGING=${QUERY_LOGGING}"
  1693. echo "INSTALL_WEB_SERVER=${INSTALL_WEB_SERVER}"
  1694. echo "INSTALL_WEB_INTERFACE=${INSTALL_WEB_INTERFACE}"
  1695. echo "LIGHTTPD_ENABLED=${LIGHTTPD_ENABLED}"
  1696. }>> "${setupVars}"
  1697.  
  1698. # Bring in the current settings and the functions to manipulate them
  1699. source "${setupVars}"
  1700. source "${PI_HOLE_LOCAL_REPO}/advanced/Scripts/webpage.sh"
  1701.  
  1702. # Look for DNS server settings which would have to be reapplied
  1703. ProcessDNSSettings
  1704.  
  1705. # Look for DHCP server settings which would have to be reapplied
  1706. ProcessDHCPSettings
  1707. }
  1708.  
  1709. # Install the logrotate script
  1710. installLogrotate() {
  1711.  
  1712. local str="Installing latest logrotate script"
  1713. echo ""
  1714. echo -ne " ${INFO} ${str}..."
  1715. # Copy the file over from the local repo
  1716. cp ${PI_HOLE_LOCAL_REPO}/advanced/Templates/logrotate /etc/pihole/logrotate
  1717. # Different operating systems have different user / group
  1718. # settings for logrotate that makes it impossible to create
  1719. # a static logrotate file that will work with e.g.
  1720. # Rasbian and Ubuntu at the same time. Hence, we have to
  1721. # customize the logrotate script here in order to reflect
  1722. # the local properties of the /var/log directory
  1723. logusergroup="$(stat -c '%U %G' /var/log)"
  1724. # If the variable has a value,
  1725. if [[ ! -z "${logusergroup}" ]]; then
  1726. #
  1727. sed -i "s/# su #/su ${logusergroup}/g;" /etc/pihole/logrotate
  1728. fi
  1729. echo -e "${OVER} ${TICK} ${str}"
  1730. }
  1731.  
  1732. # At some point in the future this list can be pruned, for now we'll need it to ensure updates don't break.
  1733. # Refactoring of install script has changed the name of a couple of variables. Sort them out here.
  1734. accountForRefactor() {
  1735. sed -i 's/piholeInterface/PIHOLE_INTERFACE/g' ${setupVars}
  1736. sed -i 's/IPv4_address/IPV4_ADDRESS/g' ${setupVars}
  1737. sed -i 's/IPv4addr/IPV4_ADDRESS/g' ${setupVars}
  1738. sed -i 's/IPv6_address/IPV6_ADDRESS/g' ${setupVars}
  1739. sed -i 's/piholeIPv6/IPV6_ADDRESS/g' ${setupVars}
  1740. sed -i 's/piholeDNS1/PIHOLE_DNS_1/g' ${setupVars}
  1741. sed -i 's/piholeDNS2/PIHOLE_DNS_2/g' ${setupVars}
  1742. sed -i 's/^INSTALL_WEB=/INSTALL_WEB_INTERFACE=/' ${setupVars}
  1743. # Add 'INSTALL_WEB_SERVER', if its not been applied already: https://github.com/pi-hole/pi-hole/pull/2115
  1744. if ! grep -q '^INSTALL_WEB_SERVER=' ${setupVars}; then
  1745. local webserver_installed=false
  1746. if grep -q '^INSTALL_WEB_INTERFACE=true' ${setupVars}; then
  1747. webserver_installed=true
  1748. fi
  1749. echo -e "INSTALL_WEB_SERVER=$webserver_installed" >> ${setupVars}
  1750. fi
  1751. }
  1752.  
  1753. # Install base files and web interface
  1754. installPihole() {
  1755. # Create the pihole user
  1756. create_pihole_user
  1757.  
  1758. # If the user wants to install the Web interface,
  1759. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  1760. if [[ ! -d "/var/www/html" ]]; then
  1761. # make the Web directory if necessary
  1762. mkdir -p /var/www/html
  1763. fi
  1764.  
  1765. if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
  1766. # Set the owner and permissions
  1767. chown ${LIGHTTPD_USER}:${LIGHTTPD_GROUP} /var/www/html
  1768. chmod 775 /var/www/html
  1769. # Give pihole access to the Web server group
  1770. usermod -a -G ${LIGHTTPD_GROUP} pihole
  1771. # If the lighttpd command is executable,
  1772. if [[ -x "$(command -v lighty-enable-mod)" ]]; then
  1773. # enable fastcgi and fastcgi-php
  1774. lighty-enable-mod fastcgi fastcgi-php > /dev/null || true
  1775. else
  1776. # Otherwise, show info about installing them
  1777. echo -e " ${INFO} Warning: 'lighty-enable-mod' utility not found
  1778. Please ensure fastcgi is enabled if you experience issues\\n"
  1779. fi
  1780. fi
  1781. fi
  1782. # For updates and unattended install.
  1783. if [[ "${useUpdateVars}" == true ]]; then
  1784. accountForRefactor
  1785. fi
  1786. # Install base files and web interface
  1787. if ! installScripts; then
  1788. echo -e " {CROSS} Failure in dependent script copy function."
  1789. exit 1
  1790. fi
  1791. # Install config files
  1792. if ! installConfigs; then
  1793. echo -e " {CROSS} Failure in dependent config copy function."
  1794. exit 1
  1795. fi
  1796. # If the user wants to install the dashboard,
  1797. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  1798. # do so
  1799. installPiholeWeb
  1800. fi
  1801. # Install the cron file
  1802. installCron
  1803. # Install the logrotate file
  1804. installLogrotate
  1805. # Check if FTL is installed
  1806. FTLdetect || echo -e " ${CROSS} FTL Engine not installed"
  1807. # Configure the firewall
  1808. if [[ "${useUpdateVars}" == false ]]; then
  1809. configureFirewall
  1810. fi
  1811.  
  1812. # install a man page entry for pihole
  1813. install_manpage
  1814.  
  1815. # Update setupvars.conf with any variables that may or may not have been changed during the install
  1816. finalExports
  1817. }
  1818.  
  1819. # SELinux
  1820. checkSelinux() {
  1821. # If the getenforce command exists,
  1822. if command -v getenforce &> /dev/null; then
  1823. # Store the current mode in a variable
  1824. enforceMode=$(getenforce)
  1825. echo -e "\\n ${INFO} SELinux mode detected: ${enforceMode}"
  1826.  
  1827. # If it's enforcing,
  1828. if [[ "${enforceMode}" == "Enforcing" ]]; then
  1829. # Explain Pi-hole does not support it yet
  1830. 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} || \
  1831. { echo -e "\\n ${COL_LIGHT_RED}SELinux Enforcing detected, exiting installer${COL_NC}"; exit 1; }
  1832. echo -e " ${INFO} Continuing installation with SELinux Enforcing
  1833. ${INFO} Please refer to official SELinux documentation to create a custom policy"
  1834. fi
  1835. fi
  1836. }
  1837.  
  1838. # Installation complete message with instructions for the user
  1839. displayFinalMessage() {
  1840. # If
  1841. if [[ "${#1}" -gt 0 ]] ; then
  1842. pwstring="$1"
  1843. # else, if the dashboard password in the setup variables exists,
  1844. elif [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) -gt 0 ]]; then
  1845. # set a variable for evaluation later
  1846. pwstring="unchanged"
  1847. else
  1848. # set a variable for evaluation later
  1849. pwstring="NOT SET"
  1850. fi
  1851. # If the user wants to install the dashboard,
  1852. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  1853. # Store a message in a variable and display it
  1854. additional="View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin
  1855.  
  1856. Your Admin Webpage login password is ${pwstring}"
  1857. fi
  1858.  
  1859. # Final completion message to user
  1860. whiptail --msgbox --backtitle "Make it so." --title "Installation Complete!" "Configure your devices to use the Pi-hole as their DNS server using:
  1861.  
  1862. IPv4: ${IPV4_ADDRESS%/*}
  1863. IPv6: ${IPV6_ADDRESS:-"Not Configured"}
  1864.  
  1865. If you set a new IP address, you should restart the Pi.
  1866.  
  1867. The install log is in /etc/pihole.
  1868.  
  1869. ${additional}" ${r} ${c}
  1870. }
  1871.  
  1872. update_dialogs() {
  1873. # If pihole -r "reconfigure" option was selected,
  1874. if [[ "${reconfigure}" = true ]]; then
  1875. # set some variables that will be used
  1876. opt1a="Repair"
  1877. opt1b="This will retain existing settings"
  1878. strAdd="You will remain on the same version"
  1879. # Otherwise,
  1880. else
  1881. # set some variables with different values
  1882. opt1a="Update"
  1883. opt1b="This will retain existing settings."
  1884. strAdd="You will be updated to the latest version."
  1885. fi
  1886. opt2a="Reconfigure"
  1887. opt2b="This will reset your Pi-hole and allow you to enter new settings."
  1888.  
  1889. # Display the information to the user
  1890. 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 \
  1891. "${opt1a}" "${opt1b}" \
  1892. "${opt2a}" "${opt2b}" 3>&2 2>&1 1>&3) || \
  1893. { echo -e " ${COL_LIGHT_RED}Cancel was selected, exiting installer${COL_NC}"; exit 1; }
  1894.  
  1895. # Set the variable based on if the user chooses
  1896. case ${UpdateCmd} in
  1897. # repair, or
  1898. ${opt1a})
  1899. echo -e " ${INFO} ${opt1a} option selected"
  1900. useUpdateVars=true
  1901. ;;
  1902. # reconfigure,
  1903. ${opt2a})
  1904. echo -e " ${INFO} ${opt2a} option selected"
  1905. useUpdateVars=false
  1906. ;;
  1907. esac
  1908. }
  1909.  
  1910. check_download_exists() {
  1911. status=$(curl --head --silent "https://ftl.pi-hole.net/${1}" | head -n 1)
  1912. if grep -q "404" <<< "$status"; then
  1913. return 1
  1914. else
  1915. return 0
  1916. fi
  1917. }
  1918.  
  1919. fully_fetch_repo() {
  1920. # Add upstream branches to shallow clone
  1921. local directory="${1}"
  1922.  
  1923. cd "${directory}" || return 1
  1924. if is_repo "${directory}"; then
  1925. git remote set-branches origin '*' || return 1
  1926. git fetch --quiet || return 1
  1927. else
  1928. return 1
  1929. fi
  1930. return 0
  1931. }
  1932.  
  1933. get_available_branches() {
  1934. # Return available branches
  1935. local directory
  1936. directory="${1}"
  1937. local output
  1938.  
  1939. cd "${directory}" || return 1
  1940. # Get reachable remote branches, but store STDERR as STDOUT variable
  1941. output=$( { git ls-remote --heads --quiet | cut -d'/' -f3- -; } 2>&1 )
  1942. echo "$output"
  1943. return
  1944. }
  1945.  
  1946. fetch_checkout_pull_branch() {
  1947. # Check out specified branch
  1948. local directory
  1949. directory="${1}"
  1950. local branch
  1951. branch="${2}"
  1952.  
  1953. # Set the reference for the requested branch, fetch, check it put and pull it
  1954. cd "${directory}" || return 1
  1955. git remote set-branches origin "${branch}" || return 1
  1956. git stash --all --quiet &> /dev/null || true
  1957. git clean --quiet --force -d || true
  1958. git fetch --quiet || return 1
  1959. checkout_pull_branch "${directory}" "${branch}" || return 1
  1960. }
  1961.  
  1962. checkout_pull_branch() {
  1963. # Check out specified branch
  1964. local directory
  1965. directory="${1}"
  1966. local branch
  1967. branch="${2}"
  1968. local oldbranch
  1969.  
  1970. cd "${directory}" || return 1
  1971.  
  1972. oldbranch="$(git symbolic-ref HEAD)"
  1973.  
  1974. str="Switching to branch: '${branch}' from '${oldbranch}'"
  1975. echo -ne " ${INFO} $str"
  1976. git checkout "${branch}" --quiet || return 1
  1977. echo -e "${OVER} ${TICK} $str"
  1978.  
  1979. git_pull=$(git pull || return 1)
  1980.  
  1981. if [[ "$git_pull" == *"up-to-date"* ]]; then
  1982. echo -e " ${INFO} ${git_pull}"
  1983. else
  1984. echo -e "$git_pull\\n"
  1985. fi
  1986.  
  1987. return 0
  1988. }
  1989.  
  1990. clone_or_update_repos() {
  1991. # If the user wants to reconfigure,
  1992. if [[ "${reconfigure}" == true ]]; then
  1993. echo " ${INFO} Performing reconfiguration, skipping download of local repos"
  1994. # Reset the Core repo
  1995. resetRepo ${PI_HOLE_LOCAL_REPO} || \
  1996. { echo -e " ${COL_LIGHT_RED}Unable to reset ${PI_HOLE_LOCAL_REPO}, exiting installer${COL_NC}"; \
  1997. exit 1; \
  1998. }
  1999. # If the Web interface was installed,
  2000. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  2001. # reset it's repo
  2002. resetRepo ${webInterfaceDir} || \
  2003. { echo -e " ${COL_LIGHT_RED}Unable to reset ${webInterfaceDir}, exiting installer${COL_NC}"; \
  2004. exit 1; \
  2005. }
  2006. fi
  2007. # Otherwise, a repair is happening
  2008. else
  2009. # so get git files for Core
  2010. getGitFiles ${PI_HOLE_LOCAL_REPO} ${piholeGitUrl} || \
  2011. { echo -e " ${COL_LIGHT_RED}Unable to clone ${piholeGitUrl} into ${PI_HOLE_LOCAL_REPO}, unable to continue${COL_NC}"; \
  2012. exit 1; \
  2013. }
  2014. # If the Web interface was installed,
  2015. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  2016. # get the Web git files
  2017. getGitFiles ${webInterfaceDir} ${webInterfaceGitUrl} || \
  2018. { echo -e " ${COL_LIGHT_RED}Unable to clone ${webInterfaceGitUrl} into ${webInterfaceDir}, exiting installer${COL_NC}"; \
  2019. exit 1; \
  2020. }
  2021. fi
  2022. fi
  2023. }
  2024.  
  2025. # Download FTL binary to random temp directory and install FTL binary
  2026. FTLinstall() {
  2027. # Local, named variables
  2028. local binary="${1}"
  2029. local latesttag
  2030. local str="Downloading and Installing FTL"
  2031. echo -ne " ${INFO} ${str}..."
  2032.  
  2033. # Find the latest version tag for FTL
  2034. latesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep "Location" | awk -F '/' '{print $NF}')
  2035. # Tags should always start with v, check for that.
  2036. if [[ ! "${latesttag}" == v* ]]; then
  2037. echo -e "${OVER} ${CROSS} ${str}"
  2038. echo -e " ${COL_LIGHT_RED}Error: Unable to get latest release location from GitHub${COL_NC}"
  2039. return 1
  2040. fi
  2041.  
  2042. # Move into the temp ftl directory
  2043. pushd "$(mktemp -d)" > /dev/null || { echo "Unable to make temporary directory for FTL binary download"; return 1; }
  2044.  
  2045. # Always replace pihole-FTL.service
  2046. install -T -m 0755 "${PI_HOLE_LOCAL_REPO}/advanced/Templates/pihole-FTL.service" "/etc/init.d/pihole-FTL"
  2047.  
  2048. local ftlBranch
  2049. local url
  2050.  
  2051. if [[ -f "/etc/pihole/ftlbranch" ]];then
  2052. ftlBranch=$(</etc/pihole/ftlbranch)
  2053. else
  2054. ftlBranch="master"
  2055. fi
  2056.  
  2057. # Determine which version of FTL to download
  2058. if [[ "${ftlBranch}" == "master" ]];then
  2059. url="https://github.com/pi-hole/FTL/releases/download/${latesttag%$'\r'}"
  2060. else
  2061. url="https://ftl.pi-hole.net/${ftlBranch}"
  2062. fi
  2063.  
  2064. # If the download worked,
  2065. if curl -sSL --fail "${url}/${binary}" -o "${binary}"; then
  2066. # get sha1 of the binary we just downloaded for verification.
  2067. curl -sSL --fail "${url}/${binary}.sha1" -o "${binary}.sha1"
  2068.  
  2069. # If we downloaded binary file (as opposed to text),
  2070. if sha1sum --status --quiet -c "${binary}".sha1; then
  2071. echo -n "transferred... "
  2072. # Stop FTL
  2073. stop_service pihole-FTL &> /dev/null
  2074. # Install the new version with the correct permissions
  2075. install -T -m 0755 "${binary}" /usr/bin/pihole-FTL
  2076. # Move back into the original directory the user was in
  2077. popd > /dev/null || { echo "Unable to return to original directory after FTL binary download."; return 1; }
  2078. # Install the FTL service
  2079. echo -e "${OVER} ${TICK} ${str}"
  2080. # dnsmasq can now be stopped and disabled if it exists
  2081. if which dnsmasq &> /dev/null; then
  2082. if check_service_active "dnsmasq";then
  2083. echo " ${INFO} FTL can now resolve DNS Queries without dnsmasq running separately"
  2084. stop_service dnsmasq
  2085. disable_service dnsmasq
  2086. fi
  2087. fi
  2088.  
  2089. #ensure /etc/dnsmasq.conf contains `conf-dir=/etc/dnsmasq.d`
  2090. confdir="conf-dir=/etc/dnsmasq.d"
  2091. conffile="/etc/dnsmasq.conf"
  2092. if ! grep -q "$confdir" "$conffile"; then
  2093. echo "$confdir" >> "$conffile"
  2094. fi
  2095.  
  2096. return 0
  2097. # Otherwise,
  2098. else
  2099. # the download failed, so just go back to the original directory
  2100. popd > /dev/null || { echo "Unable to return to original directory after FTL binary download."; return 1; }
  2101. echo -e "${OVER} ${CROSS} ${str}"
  2102. echo -e " ${COL_LIGHT_RED}Error: Download of binary from Github failed${COL_NC}"
  2103. return 1
  2104. fi
  2105. # Otherwise,
  2106. else
  2107. popd > /dev/null || { echo "Unable to return to original directory after FTL binary download."; return 1; }
  2108. echo -e "${OVER} ${CROSS} ${str}"
  2109. # The URL could not be found
  2110. echo -e " ${COL_LIGHT_RED}Error: URL not found${COL_NC}"
  2111. return 1
  2112. fi
  2113. }
  2114.  
  2115. get_binary_name() {
  2116. # This gives the machine architecture which may be different from the OS architecture...
  2117. local machine
  2118. machine=$(uname -m)
  2119.  
  2120. local str="Detecting architecture"
  2121. echo -ne " ${INFO} ${str}..."
  2122. # If the machine is arm or aarch
  2123. if [[ "${machine}" == "arm"* || "${machine}" == *"aarch"* ]]; then
  2124. # ARM
  2125. #
  2126. local rev
  2127. rev=$(uname -m | sed "s/[^0-9]//g;")
  2128. #
  2129. local lib
  2130. lib=$(ldd /bin/ls | grep -E '^\s*/lib' | awk '{ print $1 }')
  2131. #
  2132. if [[ "${lib}" == "/lib/ld-linux-aarch64.so.1" ]]; then
  2133. echo -e "${OVER} ${TICK} Detected ARM-aarch64 architecture"
  2134. # set the binary to be used
  2135. binary="pihole-FTL-aarch64-linux-gnu"
  2136. #
  2137. elif [[ "${lib}" == "/lib/ld-linux-armhf.so.3" ]]; then
  2138. #
  2139. if [[ "${rev}" -gt 6 ]]; then
  2140. echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv7+)"
  2141. # set the binary to be used
  2142. binary="pihole-FTL-arm-linux-gnueabihf"
  2143. # Otherwise,
  2144. else
  2145. echo -e "${OVER} ${TICK} Detected ARM-hf architecture (armv6 or lower) Using ARM binary"
  2146. # set the binary to be used
  2147. binary="pihole-FTL-arm-linux-gnueabi"
  2148. fi
  2149. else
  2150. echo -e "${OVER} ${TICK} Detected ARM architecture"
  2151. # set the binary to be used
  2152. binary="pihole-FTL-arm-linux-gnueabi"
  2153. fi
  2154. elif [[ "${machine}" == "ppc" ]]; then
  2155. # PowerPC
  2156. echo -e "${OVER} ${TICK} Detected PowerPC architecture"
  2157. # set the binary to be used
  2158. binary="pihole-FTL-powerpc-linux-gnu"
  2159. elif [[ "${machine}" == "x86_64" ]]; then
  2160. # This gives the architecture of packages dpkg installs (for example, "i386")
  2161. local dpkgarch
  2162. dpkgarch=$(dpkg --print-architecture 2> /dev/null)
  2163.  
  2164. # Special case: This is a 32 bit OS, installed on a 64 bit machine
  2165. # -> change machine architecture to download the 32 bit executable
  2166. if [[ "${dpkgarch}" == "i386" ]]; then
  2167. echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture"
  2168. binary="pihole-FTL-linux-x86_32"
  2169. else
  2170. # 64bit
  2171. echo -e "${OVER} ${TICK} Detected x86_64 architecture"
  2172. # set the binary to be used
  2173. binary="pihole-FTL-linux-x86_64"
  2174. fi
  2175. else
  2176. # Something else - we try to use 32bit executable and warn the user
  2177. if [[ ! "${machine}" == "i686" ]]; then
  2178. echo -e "${OVER} ${CROSS} ${str}..."
  2179. echo -e " ${INFO} ${COL_LIGHT_RED}Not able to detect architecture (unknown: ${machine}), trying 32bit executable${COL_NC}"
  2180. echo -e " ${INFO} Contact Pi-hole Support if you experience issues (e.g: FTL not running)"
  2181. else
  2182. echo -e "${OVER} ${TICK} Detected 32bit (i686) architecture"
  2183. fi
  2184. binary="pihole-FTL-linux-x86_32"
  2185. fi
  2186. }
  2187.  
  2188. FTLcheckUpdate() {
  2189. get_binary_name
  2190.  
  2191. #In the next section we check to see if FTL is already installed (in case of pihole -r).
  2192. #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
  2193. echo -e " ${INFO} Checking for existing FTL binary..."
  2194.  
  2195. local ftlLoc
  2196. ftlLoc=$(which pihole-FTL 2>/dev/null)
  2197.  
  2198. local ftlBranch
  2199.  
  2200. if [[ -f "/etc/pihole/ftlbranch" ]];then
  2201. ftlBranch=$(</etc/pihole/ftlbranch)
  2202. else
  2203. ftlBranch="master"
  2204. fi
  2205.  
  2206. local remoteSha1
  2207. local localSha1
  2208.  
  2209. # if dnsmasq exists and is running at this point, force reinstall of FTL Binary
  2210. if which dnsmasq &> /dev/null; then
  2211. if check_service_active "dnsmasq";then
  2212. return 0
  2213. fi
  2214. fi
  2215.  
  2216. if [[ ! "${ftlBranch}" == "master" ]]; then
  2217. #Check whether or not the binary for this FTL branch actually exists. If not, then there is no update!
  2218. local path
  2219. path="${ftlBranch}/${binary}"
  2220. # shellcheck disable=SC1090
  2221. if ! check_download_exists "$path"; then
  2222. echo -e " ${INFO} Branch \"${ftlBranch}\" is not available.\\n ${INFO} Use ${COL_LIGHT_GREEN}pihole checkout ftl [branchname]${COL_NC} to switch to a valid branch."
  2223. return 2
  2224. fi
  2225.  
  2226. if [[ ${ftlLoc} ]]; then
  2227. # We already have a pihole-FTL binary downloaded.
  2228. # Alt branches don't have a tagged version against them, so just confirm the checksum of the local vs remote to decide whether we download or not
  2229. remoteSha1=$(curl -sSL --fail "https://ftl.pi-hole.net/${ftlBranch}/${binary}.sha1" | cut -d ' ' -f 1)
  2230. localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1)
  2231.  
  2232. if [[ "${remoteSha1}" != "${localSha1}" ]]; then
  2233. echo -e " ${INFO} Checksums do not match, downloading from ftl.pi-hole.net."
  2234. return 0
  2235. else
  2236. echo -e " ${INFO} Checksum of installed binary matches remote. No need to download!"
  2237. return 1
  2238. fi
  2239. else
  2240. return 0
  2241. fi
  2242. else
  2243. if [[ ${ftlLoc} ]]; then
  2244. local FTLversion
  2245. FTLversion=$(/usr/bin/pihole-FTL tag)
  2246. local FTLlatesttag
  2247. FTLlatesttag=$(curl -sI https://github.com/pi-hole/FTL/releases/latest | grep 'Location' | awk -F '/' '{print $NF}' | tr -d '\r\n')
  2248.  
  2249. if [[ "${FTLversion}" != "${FTLlatesttag}" ]]; then
  2250. return 0
  2251. else
  2252. echo -e " ${INFO} Latest FTL Binary already installed (${FTLlatesttag}). Confirming Checksum..."
  2253.  
  2254. remoteSha1=$(curl -sSL --fail "https://github.com/pi-hole/FTL/releases/download/${FTLversion%$'\r'}/${binary}.sha1" | cut -d ' ' -f 1)
  2255. localSha1=$(sha1sum "$(which pihole-FTL)" | cut -d ' ' -f 1)
  2256.  
  2257. if [[ "${remoteSha1}" != "${localSha1}" ]]; then
  2258. echo -e " ${INFO} Corruption detected..."
  2259. return 0
  2260. else
  2261. echo -e " ${INFO} Checksum correct. No need to download!"
  2262. return 1
  2263. fi
  2264. fi
  2265. else
  2266. return 0
  2267. fi
  2268. fi
  2269. }
  2270.  
  2271. # Detect suitable FTL binary platform
  2272. FTLdetect() {
  2273. echo ""
  2274. echo -e " ${INFO} FTL Checks..."
  2275.  
  2276. if FTLcheckUpdate ; then
  2277. FTLinstall "${binary}" || return 1
  2278. fi
  2279.  
  2280. echo ""
  2281. }
  2282.  
  2283. make_temporary_log() {
  2284. # Create a random temporary file for the log
  2285. TEMPLOG=$(mktemp /tmp/pihole_temp.XXXXXX)
  2286. # Open handle 3 for templog
  2287. # https://stackoverflow.com/questions/18460186/writing-outputs-to-log-file-and-console
  2288. exec 3>"$TEMPLOG"
  2289. # Delete templog, but allow for addressing via file handle
  2290. # This lets us write to the log without having a temporary file on the drive, which
  2291. # is meant to be a security measure so there is not a lingering file on the drive during the install process
  2292. rm "$TEMPLOG"
  2293. }
  2294.  
  2295. copy_to_install_log() {
  2296. # Copy the contents of file descriptor 3 into the install log
  2297. # Since we use color codes such as '\e[1;33m', they should be removed
  2298. sed 's/\[[0-9;]\{1,5\}m//g' < /proc/$$/fd/3 > "${installLogLoc}"
  2299. }
  2300.  
  2301. main() {
  2302. ######## FIRST CHECK ########
  2303. # Must be root to install
  2304. local str="Root user check"
  2305. echo ""
  2306.  
  2307. # If the user's id is zero,
  2308. if [[ "${EUID}" -eq 0 ]]; then
  2309. # they are root and all is good
  2310. echo -e " ${TICK} ${str}"
  2311. # Show the Pi-hole logo so people know it's genuine since the logo and name are trademarked
  2312. show_ascii_berry
  2313. make_temporary_log
  2314. # Otherwise,
  2315. else
  2316. # They do not have enough privileges, so let the user know
  2317. echo -e " ${CROSS} ${str}"
  2318. echo -e " ${INFO} ${COL_LIGHT_RED}Script called with non-root privileges${COL_NC}"
  2319. echo -e " ${INFO} The Pi-hole requires elevated privileges to install and run"
  2320. echo -e " ${INFO} Please check the installer for any concerns regarding this requirement"
  2321. echo -e " ${INFO} Make sure to download this script from a trusted source\\n"
  2322. echo -ne " ${INFO} Sudo utility check"
  2323.  
  2324. # If the sudo command exists,
  2325. if command -v sudo &> /dev/null; then
  2326. echo -e "${OVER} ${TICK} Sudo utility check"
  2327. # Download the install script and run it with admin rights
  2328. exec curl -sSL https://raw.githubusercontent.com/pi-hole/pi-hole/master/automated%20install/basic-install.sh | sudo bash "$@"
  2329. exit $?
  2330. # Otherwise,
  2331. else
  2332. # Let them know they need to run it as root
  2333. echo -e "${OVER} ${CROSS} Sudo utility check"
  2334. echo -e " ${INFO} Sudo is needed for the Web Interface to run pihole commands\\n"
  2335. echo -e " ${INFO} ${COL_LIGHT_RED}Please re-run this installer as root${COL_NC}"
  2336. exit 1
  2337. fi
  2338. fi
  2339.  
  2340. # Check for supported distribution
  2341. distro_check
  2342.  
  2343. # If the setup variable file exists,
  2344. if [[ -f "${setupVars}" ]]; then
  2345. # if it's running unattended,
  2346. if [[ "${runUnattended}" == true ]]; then
  2347. echo -e " ${INFO} Performing unattended setup, no whiptail dialogs will be displayed"
  2348. # Use the setup variables
  2349. useUpdateVars=true
  2350. # Otherwise,
  2351. else
  2352. # show the available options (repair/reconfigure)
  2353. update_dialogs
  2354. fi
  2355. fi
  2356.  
  2357. # Start the installer
  2358. # Verify there is enough disk space for the install
  2359. if [[ "${skipSpaceCheck}" == true ]]; then
  2360. echo -e " ${INFO} Skipping free disk space verification"
  2361. else
  2362. verifyFreeDiskSpace
  2363. fi
  2364.  
  2365. # Update package cache
  2366. update_package_cache || exit 1
  2367.  
  2368. # Notify user of package availability
  2369. notify_package_updates_available
  2370.  
  2371. # Install packages used by this installation script
  2372. install_dependent_packages INSTALLER_DEPS[@]
  2373.  
  2374. # Check if SELinux is Enforcing
  2375. checkSelinux
  2376.  
  2377. if [[ "${useUpdateVars}" == false ]]; then
  2378. # Display welcome dialogs
  2379. welcomeDialogs
  2380. # Create directory for Pi-hole storage
  2381. mkdir -p /etc/pihole/
  2382. # Determine available interfaces
  2383. get_available_interfaces
  2384. # Find interfaces and let the user choose one
  2385. chooseInterface
  2386. # Decide what upstream DNS Servers to use
  2387. setDNS
  2388. # Give the user a choice of blocklists to include in their install. Or not.
  2389. chooseBlocklists
  2390. # Let the user decide if they want to block ads over IPv4 and/or IPv6
  2391. use4andor6
  2392. # Let the user decide if they want the web interface to be installed automatically
  2393. setAdminFlag
  2394. # Let the user decide if they want query logging enabled...
  2395. setLogging
  2396. else
  2397. # Source ${setupVars} to use predefined user variables in the functions
  2398. source ${setupVars}
  2399. fi
  2400. # Clone/Update the repos
  2401. clone_or_update_repos
  2402.  
  2403. # Install the Core dependencies
  2404. local dep_install_list=("${PIHOLE_DEPS[@]}")
  2405. if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
  2406. # Install the Web dependencies
  2407. dep_install_list+=("${PIHOLE_WEB_DEPS[@]}")
  2408. fi
  2409.  
  2410. install_dependent_packages dep_install_list[@]
  2411. unset dep_install_list
  2412.  
  2413. # On some systems, lighttpd is not enabled on first install. We need to enable it here if the user
  2414. # has chosen to install the web interface, else the `LIGHTTPD_ENABLED` check will fail
  2415. if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
  2416. enable_service lighttpd
  2417. fi
  2418. # Determine if lighttpd is correctly enabled
  2419. if check_service_active "lighttpd"; then
  2420. LIGHTTPD_ENABLED=true
  2421. else
  2422. LIGHTTPD_ENABLED=false
  2423. fi
  2424.  
  2425. # Install and log everything to a file
  2426. installPihole | tee -a /proc/$$/fd/3
  2427.  
  2428. # Copy the temp log file into final log location for storage
  2429. copy_to_install_log
  2430.  
  2431. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  2432. # Add password to web UI if there is none
  2433. pw=""
  2434. # If no password is set,
  2435. if [[ $(grep 'WEBPASSWORD' -c /etc/pihole/setupVars.conf) == 0 ]] ; then
  2436. # generate a random password
  2437. pw=$(tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 8)
  2438. # shellcheck disable=SC1091
  2439. . /opt/pihole/webpage.sh
  2440. echo "WEBPASSWORD=$(HashPassword ${pw})" >> ${setupVars}
  2441. fi
  2442. fi
  2443.  
  2444. # Check for and disable systemd-resolved-DNSStubListener before reloading resolved
  2445. # DNSStubListener needs to remain in place for installer to download needed files,
  2446. # so this change needs to be made after installation is complete,
  2447. # but before starting or resarting the dnsmasq or ftl services
  2448. disable_resolved_stublistener
  2449.  
  2450. # If the Web server was installed,
  2451. if [[ "${INSTALL_WEB_SERVER}" == true ]]; then
  2452.  
  2453. if [[ "${LIGHTTPD_ENABLED}" == true ]]; then
  2454. start_service lighttpd
  2455. enable_service lighttpd
  2456. else
  2457. echo -e " ${INFO} Lighttpd is disabled, skipping service restart"
  2458. fi
  2459. fi
  2460.  
  2461. echo -e " ${INFO} Restarting services..."
  2462. # Start services
  2463.  
  2464. # Enable FTL
  2465. start_service pihole-FTL
  2466. enable_service pihole-FTL
  2467.  
  2468. # Download and compile the aggregated block list
  2469. runGravity
  2470.  
  2471. # Force an update of the updatechecker
  2472. /opt/pihole/updatecheck.sh
  2473. /opt/pihole/updatecheck.sh x remote
  2474.  
  2475. if [[ "${useUpdateVars}" == false ]]; then
  2476. displayFinalMessage "${pw}"
  2477. fi
  2478.  
  2479. # If the Web interface was installed,
  2480. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  2481. # If there is a password,
  2482. if (( ${#pw} > 0 )) ; then
  2483. # display the password
  2484. echo -e " ${INFO} Web Interface password: ${COL_LIGHT_GREEN}${pw}${COL_NC}"
  2485. echo -e " ${INFO} This can be changed using 'pihole -a -p'\\n"
  2486. fi
  2487. fi
  2488.  
  2489. if [[ "${useUpdateVars}" == false ]]; then
  2490. # If the Web interface was installed,
  2491. if [[ "${INSTALL_WEB_INTERFACE}" == true ]]; then
  2492. echo -e " ${INFO} View the web interface at http://pi.hole/admin or http://${IPV4_ADDRESS%/*}/admin"
  2493. echo ""
  2494. fi
  2495. # Explain to the user how to use Pi-hole as their DNS server
  2496. echo -e " ${INFO} You may now configure your devices to use the Pi-hole as their DNS server"
  2497. [[ -n "${IPV4_ADDRESS%/*}" ]] && echo -e " ${INFO} Pi-hole DNS (IPv4): ${IPV4_ADDRESS%/*}"
  2498. [[ -n "${IPV6_ADDRESS}" ]] && echo -e " ${INFO} Pi-hole DNS (IPv6): ${IPV6_ADDRESS}"
  2499. echo -e " ${INFO} If you set a new IP address, please restart the server running the Pi-hole"
  2500. INSTALL_TYPE="Installation"
  2501. else
  2502. INSTALL_TYPE="Update"
  2503. fi
  2504.  
  2505. # Display where the log file is
  2506. echo -e "\\n ${INFO} The install log is located at: ${installLogLoc}
  2507. ${COL_LIGHT_GREEN}${INSTALL_TYPE} Complete! ${COL_NC}"
  2508.  
  2509. if [[ "${INSTALL_TYPE}" == "Update" ]]; then
  2510. echo ""
  2511. /usr/local/bin/pihole version --current
  2512. fi
  2513. }
  2514.  
  2515. if [[ "${PH_TEST}" != true ]] ; then
  2516. main "$@"
  2517. fi
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement