Advertisement
Guest User

Untitled

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