Guest User

Untitled

a guest
Feb 23rd, 2023
28
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 67.73 KB | None | 0 0
  1. #!/usr/bin/env sh
  2. # shellcheck disable=SC1091
  3.  
  4. # Ignore warning about `local` being undefinded in POSIX
  5. # shellcheck disable=SC3043
  6. # https://github.com/koalaman/shellcheck/wiki/SC3043#exceptions
  7.  
  8. # PADD
  9. # A more advanced version of the chronometer provided with Pihole
  10.  
  11. # SETS LOCALE
  12. export LC_ALL=C
  13. export LC_NUMERIC=C
  14.  
  15. ######################### VARIABLES FOR 800X480 DISPLAY, TERMINUS FONT 15PT SIZE ####################
  16. version=$(tail -1 /proc/cpuinfo | awk 'NR==1{print $5$7, $8, $9}')
  17. SoC=$(grep -a "Hardware" /proc/cpuinfo) | awk 'NR==1{print $NF}'
  18. disk_used=$(df -Th | awk 'NR==2{gsub(/[^0-9. ]/,"");print $3}') #disk_used=$(df -Th | awk 'NR==2{print $4}')
  19. disk_total=$(df -Th | awk 'NR==2{gsub(/[^0-9. ]/,"");print $2}')
  20. disk_perc=$(df -Th | awk 'NR==2{print $6}')
  21. installed_packages=$(dpkg --get-selections | wc -l)
  22. run_process=$(echo $(( $(pstree -Ta | wc -l) - 3 )))
  23. boot_time=$(systemd-analyze | mawk 'NR==1{gsub(/[^0-9.]/,"");printf "%.3f", $1 }')
  24. dietpi_version=$(cat $G_DIETPI_VERSION_CORE.$G_DIETPI_VERSION_SUB.$G_DIETPI_VERSION_RC)
  25. log_size=$(df -Th | awk 'NR==8{print $4}')
  26. log_perc=$(df -Th | awk 'NR==8{print $6}')
  27. unbound=$(sudo unbound-control status | awk 'NR==1{print $NF}')
  28. #sys_cores=$(grep -c "^processor" /proc/cpuinfo)
  29. cpu_freq=$(lscpu | awk '/CPU max MHz/{if($NF+0>1000)printf "%0.2f GHz\n",$NF/1000; else printf "%.3f MHz\n",$NF}')
  30. ram_total=$(awk '/MemTotal/ {printf( "%.f\n", $2 / 1024 / 1024 )}' /proc/meminfo)
  31. ram_free=$(free -th | mawk '/^Total:/{print $4}')
  32. ram_used=$(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.0f %.0f %.0f", (total-free-buffers-cached)*100/total, (total-free-buffers-cached)*1024, total*1024}' /proc/meminfo) #(free -m | awk 'NR==2{printf "%s",$3 }')
  33. ext_ip=$(dig +short myip.opendns.com @resolver1.opendns.com)
  34.  
  35. ############################################ VARIABLES #############################################
  36.  
  37. # VERSION
  38. padd_version="v3.10.1"
  39.  
  40. # LastChecks
  41. LastCheckVersionInformation=$(date +%s)
  42. LastCheckNetworkInformation=$(date +%s)
  43. LastCheckSummaryInformation=$(date +%s)
  44. LastCheckPiholeInformation=$(date +%s)
  45. LastCheckSystemInformation=$(date +%s)
  46. LastCheckPADDInformation=$(date +%s)
  47.  
  48. # CORES
  49. core_count=$(nproc --all 2> /dev/null)
  50.  
  51. # COLORS
  52. CSI="$(printf '\033')[" # Control Sequence Introducer
  53. red_text="${CSI}91m" # Red
  54. green_text="${CSI}92m" # Green
  55. yellow_text="${CSI}93m" # Yellow
  56. blue_text="${CSI}94m" # Blue
  57. magenta_text="${CSI}95m" # Magenta
  58. cyan_text="${CSI}96m" # Cyan
  59. reset_text="${CSI}0m" # Reset to default
  60. clear_line="${CSI}0K" # Clear the current line to the right to wipe any artifacts remaining from last print
  61.  
  62. # STYLES
  63. bold_text="${CSI}1m"
  64. blinking_text="${CSI}5m"
  65. dim_text="${CSI}2m"
  66.  
  67. # CHECK BOXES
  68. check_box_good="[${green_text}✓${reset_text}]" # Good
  69. check_box_bad="[${red_text}✗${reset_text}]" # Bad
  70. check_box_question="[${yellow_text}?${reset_text}]" # Question / ?
  71. check_box_info="[${yellow_text}i${reset_text}]" # Info / i
  72.  
  73. # PICO STATUSES
  74. pico_status_ok="${check_box_good} Sys. OK"
  75. pico_status_update="${check_box_info} Update"
  76. pico_status_hot="${check_box_bad} Sys. Hot!"
  77. pico_status_off="${check_box_info} No blck"
  78. pico_status_ftl_down="${check_box_bad} FTL Down"
  79. pico_status_dns_down="${check_box_bad} DNS Down"
  80. pico_status_unknown="${check_box_question} Stat. Unk."
  81.  
  82. # MINI STATUS
  83. mini_status_ok="${check_box_good} System OK"
  84. mini_status_update="${check_box_info} Update avail."
  85. mini_status_hot="${check_box_bad} System is hot!"
  86. mini_status_off="${check_box_info} No blocking!"
  87. mini_status_ftl_down="${check_box_bad} FTL down!"
  88. mini_status_dns_down="${check_box_bad} DNS off!"
  89. mini_status_unknown="${check_box_question} Status unknown"
  90.  
  91. # REGULAR STATUS
  92. full_status_ok="${check_box_good} System is healthy"
  93. full_status_update="${check_box_info} Updates are available"
  94. full_status_hot="${check_box_bad} System is hot!"
  95. full_status_off="${check_box_info} Blocking is disabled"
  96. full_status_ftl_down="${check_box_bad} FTL is down!"
  97. full_status_dns_down="${check_box_bad} DNS is off!"
  98. full_status_unknown="${check_box_question} Status unknown!"
  99.  
  100. # MEGA STATUS
  101. mega_status_ok="${check_box_good} Your system is healthy"
  102. mega_status_update="${check_box_info} Updates are available"
  103. mega_status_hot="${check_box_bad} Your system is hot!"
  104. mega_status_off="${check_box_info} Blocking is disabled!"
  105. mega_status_ftl_down="${check_box_bad} FTLDNS service is not running!"
  106. mega_status_dns_down="${check_box_bad} Pi-hole's DNS server is off!"
  107. mega_status_unknown="${check_box_question} Unable to determine Pi-hole status!"
  108.  
  109. # TINY STATUS
  110. tiny_status_ok="${check_box_good} System is healthy"
  111. tiny_status_update="${check_box_info} Updates are available"
  112. tiny_status_hot="${check_box_bad} System is hot!"
  113. tiny_status_off="${check_box_info} Blocking is disabled"
  114. tiny_status_ftl_down="${check_box_bad} FTL is down!"
  115. tiny_status_dns_down="${check_box_bad} DNS is off!"
  116. tiny_status_unknown="${check_box_question} Status unknown!"
  117.  
  118. # Text only "logos"
  119. padd_text="${green_text}${bold_text}PADD${reset_text}"
  120.  
  121. # PADD logos - regular and retro
  122. padd_logo_1="${bold_text}${green_text} __ __ __ ${reset_text}"
  123. padd_logo_2="${bold_text}${green_text}|__) /\\ | \\| \\ ${reset_text}"
  124. padd_logo_3="${bold_text}${green_text}| /--\\|__/|__/ ${reset_text}"
  125. padd_logo_retro_1="${bold_text} ${yellow_text}_${green_text}_ ${blue_text}_${magenta_text}_ ${yellow_text}_${green_text}_ ${reset_text}"
  126. padd_logo_retro_2="${bold_text}${yellow_text}|${green_text}_${blue_text}_${cyan_text}) ${red_text}/${yellow_text}\\ ${blue_text}| ${red_text}\\${yellow_text}| ${cyan_text}\\ ${reset_text}"
  127. padd_logo_retro_3="${bold_text}${green_text}| ${red_text}/${yellow_text}-${green_text}-${blue_text}\\${cyan_text}|${magenta_text}_${red_text}_${yellow_text}/${green_text}|${blue_text}_${cyan_text}_${magenta_text}/ ${reset_text}"
  128.  
  129.  
  130. ############################################# GETTERS ##############################################
  131.  
  132. GetFTLData() {
  133. local ftl_port data
  134. ftl_port=$(getFTLAPIPort)
  135. if [ -n "$ftl_port" ]; then
  136. # Send command to FTL and ask to quit when finished
  137. data="$(echo ">$1 >quit" | nc 127.0.0.1 "${ftl_port}")"
  138. echo "${data}"
  139. fi
  140. }
  141.  
  142. GetSummaryInformation() {
  143. summary=$(GetFTLData "stats")
  144. cache_info=$(GetFTLData "cacheinfo")
  145.  
  146. clients=$(echo "${summary}" | grep "unique_clients" | grep -Eo "[0-9]+$")
  147.  
  148. blocking_status=$(echo "${summary}" | grep "status" | grep -Eo "enabled|disabled|unknown" )
  149.  
  150. domains_being_blocked_raw=$(echo "${summary}" | grep "domains_being_blocked" | grep -Eo "[0-9]+$")
  151. domains_being_blocked=$(printf "%.f" "${domains_being_blocked_raw}")
  152.  
  153. dns_queries_today_raw=$(echo "$summary" | grep "dns_queries_today" | grep -Eo "[0-9]+$")
  154. dns_queries_today=$(printf "%.f" "${dns_queries_today_raw}")
  155.  
  156. ads_blocked_today_raw=$(echo "$summary" | grep "ads_blocked_today" | grep -Eo "[0-9]+$")
  157. ads_blocked_today=$(printf "%.f" "${ads_blocked_today_raw}")
  158.  
  159. ads_percentage_today_raw=$(echo "$summary" | grep "ads_percentage_today" | grep -Eo "[0-9.]+$")
  160. ads_percentage_today=$(printf "%.1f" "${ads_percentage_today_raw}")
  161.  
  162. cache_size=$(echo "$cache_info" | grep "cache-size" | grep -Eo "[0-9.]+$")
  163. cache_deletes=$(echo "$cache_info" | grep "cache-live-freed" | grep -Eo "[0-9.]+$")
  164. cache_inserts=$(echo "$cache_info"| grep "cache-inserted" | grep -Eo "[0-9.]+$")
  165.  
  166. latest_blocked=$(GetFTLData recentBlocked)
  167.  
  168. top_blocked=$(GetFTLData "top-ads (1)" | awk '{print $3}')
  169.  
  170. top_domain=$(GetFTLData "top-domains (1)" | awk '{print $3}')
  171.  
  172. top_client_raw=$(GetFTLData "top-clients (1)" | awk '{print $4}')
  173. if [ -z "${top_client_raw}" ]; then
  174. top_client=$(GetFTLData "top-clients (1)" | awk '{print $3}')
  175. else
  176. top_client="${top_client_raw}"
  177. fi
  178. }
  179.  
  180. GetSystemInformation() {
  181. # System uptime
  182. system_uptime_raw=$(uptime)
  183.  
  184. # CPU temperature
  185. if [ -d "/sys/devices/platform/coretemp.0/hwmon/" ]; then
  186. cpu=$(cat "$(find /sys/devices/platform/coretemp.0/hwmon/ -maxdepth 2 -name "temp1_input" 2>/dev/null | head -1)")
  187. elif [ -f /sys/class/thermal/thermal_zone0/temp ]; then
  188. cpu=$(cat /sys/class/thermal/thermal_zone0/temp)
  189. elif [ -f /sys/class/hwmon/hwmon0/temp1_input ]; then
  190. cpu=$(cat /sys/class/hwmon/hwmon0/temp1_input)
  191. else
  192. cpu=0
  193. fi
  194.  
  195. # Convert CPU temperature to correct unit
  196. if [ "${TEMPERATUREUNIT}" = "F" ]; then
  197. temperature="$(printf %.1f "$(echo "${cpu}" | awk '{print $1 * 9 / 5000 + 32}')")°F"
  198. elif [ "${TEMPERATUREUNIT}" = "K" ]; then
  199. temperature="$(printf %.1f "$(echo "${cpu}" | awk '{print $1 / 1000 + 273.15}')")°K"
  200. else
  201. temperature="$(printf %.1f "$(echo "${cpu}" | awk '{print $1 / 1000}')")°C"
  202. fi
  203.  
  204. # CPU load, heatmap
  205. cpu_load_1=$(awk '{print $1}' < /proc/loadavg)
  206. cpu_load_5=$(awk '{print $2}' < /proc/loadavg)
  207. cpu_load_15=$(awk '{print $3}' < /proc/loadavg)
  208. cpu_load_1_heatmap=$(HeatmapGenerator "${cpu_load_1}" "${core_count}")
  209. cpu_load_5_heatmap=$(HeatmapGenerator "${cpu_load_5}" "${core_count}")
  210. cpu_load_15_heatmap=$(HeatmapGenerator "${cpu_load_15}" "${core_count}")
  211. cpu_percent=$(printf %.1f "$(echo "${cpu_load_1} ${core_count}" | awk '{print ($1 / $2) * 100}')")
  212.  
  213. # CPU temperature heatmap
  214. hot_flag=false
  215. # If we're getting close to 85°C... (https://www.raspberrypi.org/blog/introducing-turbo-mode-up-to-50-more-performance-for-free/)
  216. if [ ${cpu} -gt 80000 ]; then
  217. temp_heatmap=${blinking_text}${red_text}
  218. # set flag to change the status message in SetStatusMessage()
  219. hot_flag=true
  220. elif [ ${cpu} -gt 70000 ]; then
  221. temp_heatmap=${magenta_text}
  222. elif [ ${cpu} -gt 60000 ]; then
  223. temp_heatmap=${blue_text}
  224. else
  225. temp_heatmap=${cyan_text}
  226. fi
  227.  
  228. # Memory use, heatmap and bar
  229. memory_percent=$(awk '/MemTotal:/{total=$2} /MemFree:/{free=$2} /Buffers:/{buffers=$2} /^Cached:/{cached=$2} END {printf "%.1f", (total-free-buffers-cached)*100/total}' '/proc/meminfo')
  230. memory_heatmap=$(HeatmapGenerator "${memory_percent}")
  231.  
  232. # Get product name and family
  233. product_name=
  234. product_family=
  235. if [ -f /sys/devices/virtual/dmi/id/product_name ]; then
  236. # Get product name, remove possible null byte
  237. product_name=$(tr -d '\0' < /sys/devices/virtual/dmi/id/product_name)
  238. fi
  239. if [ -f /sys/devices/virtual/dmi/id/product_family ]; then
  240. # Get product family, remove possible null byte
  241. product_family=$(tr -d '\0' < /sys/devices/virtual/dmi/id/product_family)
  242. fi
  243.  
  244. board_vendor=
  245. board_name=
  246. if [ -f /sys/devices/virtual/dmi/id/board_vendor ]; then
  247. board_vendor=$(tr -d '\0' < /sys/devices/virtual/dmi/id/board_vendor)
  248. fi
  249. if [ -f /sys/devices/virtual/dmi/id/board_name ]; then
  250. board_name="$(tr -d '\0' < /sys/devices/virtual/dmi/id/board_name)"
  251. fi
  252.  
  253.  
  254. if [ -n "$product_name" ] || [ -n "$product_family" ]; then
  255. if echo "$product_family" | grep -q "$product_name"; then
  256. # If product_name is contained in product_family, only show product_family
  257. sys_model="${product_family}"
  258. else
  259. # If product_name is not contained in product_family, both are shown
  260. sys_model="${product_family} ${product_name}"
  261. fi
  262. elif [ -f /sys/firmware/devicetree/base/model ]; then
  263. sys_model=$(tr -d '\0' < /sys/firmware/devicetree/base/model)
  264. elif [ -n "$board_vendor" ] || [ -n "$board_name" ]; then
  265. sys_model="${board_vendor} ${board_name}"
  266. elif [ -f /tmp/sysinfo/model ]; then
  267. sys_model=$(tr -d '\0' < /tmp/sysinfo/model)
  268. elif [ -n "${DOCKER_VERSION}" ]; then
  269. # Docker image. DOCKER_VERSION is read from /etc/pihole/versions
  270. sys_model="Container"
  271. fi
  272.  
  273. # Cleaning device model from useless OEM information
  274. sys_model=$(filterModel "${sys_model}")
  275.  
  276. if [ -z "$sys_model" ]; then
  277. sys_model="Unknown"
  278. fi
  279. }
  280.  
  281. GetNetworkInformation() {
  282. # Get pi IPv4 address
  283. pi_ip4_addrs="$(ip addr | grep 'inet ' | grep -v '127.0.0.1/8' | awk '{print $2}' | cut -f1 -d'/' |wc -l)"
  284. if [ "${pi_ip4_addrs}" -eq 0 ]; then
  285. # No IPv4 address available
  286. pi_ip4_addr="N/A"
  287. elif [ "${pi_ip4_addrs}" -eq 1 ]; then
  288. # One IPv4 address available
  289. pi_ip4_addr="$(ip addr | grep 'inet ' | grep -v '127.0.0.1/8' | awk '{print $2}' | cut -f1 -d'/' | head -n 1)"
  290. else
  291. # More than one IPv4 address available
  292. pi_ip4_addr="$(ip addr | grep 'inet ' | grep -v '127.0.0.1/8' | awk '{print $2}' | cut -f1 -d'/' | head -n 1)+"
  293. fi
  294.  
  295. # Get pi IPv6 address
  296. pi_ip6_addrs="$(ip addr | grep 'inet6 ' | grep -v '::1/128' | awk '{print $2}' | cut -f1 -d'/' | wc -l)"
  297. if [ "${pi_ip6_addrs}" -eq 0 ]; then
  298. # No IPv6 address available
  299. pi_ip6_addr="N/A"
  300. ipv6_check_box=${check_box_bad}
  301. elif [ "${pi_ip6_addrs}" -eq 1 ]; then
  302. # One IPv6 address available
  303. pi_ip6_addr="$(ip addr | grep 'inet6 ' | grep -v '::1/128' | awk '{print $2}' | cut -f1 -d'/' | head -n 1)"
  304. ipv6_check_box=${check_box_good}
  305. else
  306. # More than one IPv6 address available
  307. pi_ip6_addr="$(ip addr | grep 'inet6 ' | grep -v '::1/128' | awk '{print $2}' | cut -f1 -d'/' | head -n 1)+"
  308. ipv6_check_box=${check_box_good}
  309. fi
  310.  
  311. # Get hostname and gateway
  312. pi_hostname=$(hostname)
  313.  
  314. full_hostname=${pi_hostname}
  315. # does the Pi-hole have a domain set?
  316. if [ -n "${PIHOLE_DOMAIN+x}" ]; then
  317. # is Pi-hole acting as DHCP server?
  318. if [ "${DHCP_ACTIVE}" = "true" ]; then
  319. count=${pi_hostname}"."${PIHOLE_DOMAIN}
  320. count=${#count}
  321. if [ "${count}" -lt "18" ]; then
  322. full_hostname=${pi_hostname}"."${PIHOLE_DOMAIN}
  323. fi
  324. fi
  325. fi
  326.  
  327. # Get the DNS count (from pihole -c)
  328. dns_count="0"
  329. [ -n "${PIHOLE_DNS_1}" ] && dns_count=$((dns_count+1))
  330. [ -n "${PIHOLE_DNS_2}" ] && dns_count=$((dns_count+1))
  331. [ -n "${PIHOLE_DNS_3}" ] && dns_count=$((dns_count+1))
  332. [ -n "${PIHOLE_DNS_4}" ] && dns_count=$((dns_count+1))
  333. [ -n "${PIHOLE_DNS_5}" ] && dns_count=$((dns_count+1))
  334. [ -n "${PIHOLE_DNS_6}" ] && dns_count=$((dns_count+1))
  335. [ -n "${PIHOLE_DNS_7}" ] && dns_count=$((dns_count+1))
  336. [ -n "${PIHOLE_DNS_8}" ] && dns_count=$((dns_count+1))
  337. [ -n "${PIHOLE_DNS_9}" ] && dns_count=$((dns_count+1))
  338.  
  339. # if there's only one DNS server
  340. if [ ${dns_count} -eq 1 ]; then
  341. dns_information="1 server"
  342. else
  343. dns_information="${dns_count} servers"
  344. fi
  345.  
  346. # Is Pi-Hole acting as the DHCP server?
  347. if [ "${DHCP_ACTIVE}" = "true" ]; then
  348. dhcp_status="Enabled"
  349. dhcp_info=" Range: ${DHCP_START} - ${DHCP_END}"
  350. dhcp_heatmap=${green_text}
  351. dhcp_check_box=${check_box_good}
  352.  
  353. # Is DHCP handling IPv6?
  354. # DHCP_IPv6 is set in setupVars.conf
  355. # shellcheck disable=SC2154
  356. if [ "${DHCP_IPv6}" = "true" ]; then
  357. dhcp_ipv6_status="Enabled"
  358. dhcp_ipv6_heatmap=${green_text}
  359. dhcp_ipv6_check_box=${check_box_good}
  360. else
  361. dhcp_ipv6_status="Disabled"
  362. dhcp_ipv6_heatmap=${red_text}
  363. dhcp_ipv6_check_box=${check_box_bad}
  364. fi
  365. else
  366. dhcp_status="Disabled"
  367. dhcp_heatmap=${red_text}
  368. dhcp_check_box=${check_box_bad}
  369.  
  370. # if the DHCP Router variable isn't set
  371. if [ -z ${DHCP_ROUTER+x} ]; then
  372. DHCP_ROUTER=$(GetFTLData "gateway" | awk '{ printf $1 }')
  373. fi
  374.  
  375. dhcp_info=" Router: ${DHCP_ROUTER}"
  376. dhcp_heatmap=${red_text}
  377. dhcp_check_box=${check_box_bad}
  378.  
  379. dhcp_ipv6_status="N/A"
  380. dhcp_ipv6_heatmap=${yellow_text}
  381. dhcp_ipv6_check_box=${check_box_question}
  382. fi
  383.  
  384. # DNSSEC
  385. if [ "${DNSSEC}" = "true" ]; then
  386. dnssec_status="Enabled"
  387. dnssec_heatmap=${green_text}
  388. else
  389. dnssec_status="Disabled"
  390. dnssec_heatmap=${red_text}
  391. fi
  392.  
  393. # Conditional forwarding
  394. if [ "${CONDITIONAL_FORWARDING}" = "true" ] || [ "${REV_SERVER}" = "true" ]; then
  395. conditional_forwarding_status="Enabled"
  396. conditional_forwarding_heatmap=${green_text}
  397. else
  398. conditional_forwarding_status="Disabled"
  399. conditional_forwarding_heatmap=${red_text}
  400. fi
  401.  
  402. #Default interface data
  403. def_iface_data=$(GetFTLData "interfaces" | head -n1)
  404. iface_name="$(echo "$def_iface_data" | awk '{print $1}')"
  405. tx_bytes="$(echo "$def_iface_data" | awk '{print $4}')"
  406. rx_bytes="$(echo "$def_iface_data" | awk '{print $5}')"
  407. }
  408.  
  409. GetPiholeInformation() {
  410. # Get FTL status
  411.  
  412. # Get FTL's current PID
  413. ftlPID="$(getFTLPID)"
  414.  
  415. # If FTL is not running (getFTLPID returns -1), set all variables to "not running"
  416. ftl_down_flag=false
  417. if [ "${ftlPID}" = "-1" ]; then
  418. ftl_status="Not running"
  419. ftl_heatmap=${red_text}
  420. ftl_check_box=${check_box_bad}
  421. # set flag to change the status message in SetStatusMessage()
  422. ftl_down_flag=true
  423. ftl_cpu="N/A"
  424. ftl_mem_percentage="N/A"
  425. else
  426. ftl_status="Running"
  427. ftl_heatmap=${green_text}
  428. ftl_check_box=${check_box_good}
  429. # Get FTL CPU and memory usage
  430. ftl_cpu="$(ps h -p "${ftlPID}" -o %cpu | tr -d '[:space:]')%"
  431. ftl_mem_percentage="$(ps h -p "${ftlPID}" -o %mem | tr -d '[:space:]')%"
  432. # Get Pi-hole (blocking) status
  433. ftl_dns_port=$(GetFTLData "dns-port")
  434. fi
  435.  
  436. # ${ftl_dns_port} == 0 DNS server part of dnsmasq disabled, ${ftl_status} == "Not running" no ftlPID found
  437. dns_down_flag=false
  438. if [ "${ftl_dns_port}" = 0 ] || [ "${ftl_status}" = "Not running" ]; then
  439. dns_status="DNS offline"
  440. dns_heatmap=${red_text}
  441. dns_check_box=${check_box_bad}
  442. # set flag to change the status message in SetStatusMessage()
  443. dns_down_flag=true
  444. else
  445. dns_check_box=${check_box_good}
  446. dns_status="Active"
  447. dns_heatmap=${green_text}
  448. fi
  449. }
  450.  
  451. GetVersionInformation() {
  452. # Check if version status has been saved
  453. # all info is sourced from /etc/pihole/versions
  454.  
  455. out_of_date_flag=false
  456.  
  457. # If PADD is running inside docker, immediately return without checking for updated component versions
  458. if [ -n "${DOCKER_VERSION}" ]; then
  459. docker_version_converted="$(VersionConverter "${DOCKER_VERSION}")"
  460. docker_version_latest_converted="$(VersionConverter "${GITHUB_DOCKER_VERSION}")"
  461.  
  462. if [ "${docker_version_converted}" -lt "${docker_version_latest_converted}" ]; then
  463. out_of_date_flag="true"
  464. docker_version_heatmap=${red_text}
  465. else
  466. docker_version_heatmap=${green_text}
  467. fi
  468. return
  469. fi
  470.  
  471. # Gather CORE version information...
  472. # Extract vx.xx or vx.xx.xxx version
  473. CORE_VERSION="$(echo "${CORE_VERSION}" | grep -oE '^v[0-9]+([.][0-9]+){1,2}')"
  474. if [ "${CORE_BRANCH}" = "master" ]; then
  475. core_version_converted="$(VersionConverter "${CORE_VERSION}")"
  476. core_version_latest_converted=$(VersionConverter "${GITHUB_CORE_VERSION}")
  477.  
  478. if [ "${core_version_converted}" -lt "${core_version_latest_converted}" ]; then
  479. out_of_date_flag="true"
  480. core_version_heatmap=${red_text}
  481. else
  482. core_version_heatmap=${green_text}
  483. fi
  484.  
  485. else
  486. # Custom branch
  487. if [ -z "${CORE_BRANCH}" ]; then
  488. # Branch name is empty, something went wrong
  489. core_version_heatmap=${red_text}
  490. CORE_VERSION="?"
  491. else
  492. if [ "${CORE_HASH}" = "${GITHUB_CORE_HASH}" ]; then
  493. # up-to-date
  494. core_version_heatmap=${green_text}
  495. else
  496. # out-of-date
  497. out_of_date_flag="true"
  498. core_version_heatmap=${red_text}
  499. fi
  500. # shorten common branch names (fix/, tweak/, new/)
  501. # use the first 7 characters of the branch name as version
  502. CORE_VERSION="$(printf '%s' "$CORE_BRANCH" | sed 's/fix\//f\//;s/new\//n\//;s/tweak\//t\//' | cut -c 1-7)"
  503. fi
  504. fi
  505.  
  506. # Gather web version information...
  507. # Extract vx.xx or vx.xx.xxx version
  508. if [ "$INSTALL_WEB_INTERFACE" = true ]; then
  509. WEB_VERSION="$(echo "${WEB_VERSION}" | grep -oE '^v[0-9]+([.][0-9]+){1,2}')"
  510. if [ "${WEB_BRANCH}" = "master" ]; then
  511. web_version_converted="$(VersionConverter "${WEB_VERSION}")"
  512. web_version_latest_converted=$(VersionConverter "${GITHUB_WEB_VERSION}")
  513.  
  514. if [ "${web_version_converted}" -lt "${web_version_latest_converted}" ]; then
  515. out_of_date_flag="true"
  516. web_version_heatmap=${red_text}
  517. else
  518. web_version_heatmap=${green_text}
  519. fi
  520.  
  521. else
  522. # Custom branch
  523. if [ -z "${WEB_BRANCH}" ]; then
  524. # Branch name is empty, something went wrong
  525. web_version_heatmap=${red_text}
  526. WEB_VERSION="?"
  527. else
  528. if [ "${WEB_HASH}" = "${GITHUB_WEB_HASH}" ]; then
  529. # up-to-date
  530. web_version_heatmap=${green_text}
  531. else
  532. # out-of-date
  533. out_of_date_flag="true"
  534. web_version_heatmap=${red_text}
  535. fi
  536. # shorten common branch names (fix/, tweak/, new/)
  537. # use the first 7 characters of the branch name as version
  538. WEB_VERSION="$(printf '%s' "$WEB_BRANCH" | sed 's/fix\//f\//;s/new\//n\//;s/tweak\//t\//' | cut -c 1-7)"
  539. fi
  540. fi
  541. else
  542. # Web interface not installed
  543. WEB_VERSION="N/A"
  544. web_version_heatmap=${yellow_text}
  545. fi
  546.  
  547. # Gather FTL version information...
  548. # Extract vx.xx or vx.xx.xxx version
  549. FTL_VERSION="$(echo "${FTL_VERSION}" | grep -oE '^v[0-9]+([.][0-9]+){1,2}')"
  550. if [ "${FTL_BRANCH}" = "master" ]; then
  551. ftl_version_converted="$(VersionConverter "${FTL_VERSION}")"
  552. ftl_version_latest_converted=$(VersionConverter "${GITHUB_FTL_VERSION}")
  553.  
  554. if [ "${ftl_version_converted}" -lt "${ftl_version_latest_converted}" ]; then
  555. out_of_date_flag="true"
  556. ftl_version_heatmap=${red_text}
  557. else
  558. ftl_version_heatmap=${green_text}
  559. fi
  560. else
  561. # Custom branch
  562. if [ -z "${FTL_BRANCH}" ]; then
  563. # Branch name is empty, something went wrong
  564. ftl_version_heatmap=${red_text}
  565. FTL_VERSION="?"
  566. else
  567. if [ "${FTL_HASH}" = "${GITHUB_FTL_HASH}" ]; then
  568. # up-to-date
  569. ftl_version_heatmap=${green_text}
  570. else
  571. # out-of-date
  572. out_of_date_flag="true"
  573. ftl_version_heatmap=${red_text}
  574. fi
  575. # shorten common branch names (fix/, tweak/, new/)
  576. # use the first 7 characters of the branch name as version
  577. FTL_VERSION="$(printf '%s' "$FTL_BRANCH" | sed 's/fix\//f\//;s/new\//n\//;s/tweak\//t\//' | cut -c 1-7)"
  578. fi
  579. fi
  580.  
  581. }
  582.  
  583. GetPADDInformation() {
  584. # If PADD is running inside docker, immediately return without checking for an update
  585. if [ -n "${DOCKER_VERSION}" ]; then
  586. return
  587. fi
  588.  
  589. # PADD version information...
  590. padd_version_latest="$(curl --silent https://api.github.com/repos/pi-hole/PADD/releases/latest | grep '"tag_name":' | awk -F \" '{print $4}')"
  591. # is PADD up-to-date?
  592. padd_out_of_date_flag=false
  593. if [ -z "${padd_version_latest}" ]; then
  594. padd_version_heatmap=${yellow_text}
  595. else
  596. padd_version_latest_converted="$(VersionConverter "${padd_version_latest}")"
  597. padd_version_converted=$(VersionConverter "${padd_version}")
  598.  
  599. if [ "${padd_version_converted}" -lt "${padd_version_latest_converted}" ]; then
  600. padd_out_of_date_flag="true"
  601. padd_version_heatmap=${red_text}
  602. else
  603. # local and remote PADD version match or local is newer
  604. padd_version_heatmap=${green_text}
  605. fi
  606. fi
  607. }
  608.  
  609. GenerateSizeDependendOutput() {
  610. if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then
  611. ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 10 "color")
  612. elif [ "$1" = "mini" ]; then
  613. ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 20 "color")
  614.  
  615. if [ ${#latest_blocked} -gt 30 ]; then
  616. latest_blocked=$(echo "$latest_blocked" | cut -c1-27)"..."
  617. fi
  618.  
  619. if [ ${#top_blocked} -gt 30 ]; then
  620. top_blocked=$(echo "$top_blocked" | cut -c1-27)"..."
  621. fi
  622. elif [ "$1" = "tiny" ]; then
  623. ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 30 "color")
  624.  
  625. if [ ${#latest_blocked} -gt 38 ]; then
  626. latest_blocked=$(echo "$latest_blocked" | cut -c1-38)"..."
  627. fi
  628.  
  629. if [ ${#top_blocked} -gt 38 ]; then
  630. top_blocked=$(echo "$top_blocked" | cut -c1-38)"..."
  631. fi
  632.  
  633. if [ ${#top_domain} -gt 38 ]; then
  634. top_domain=$(echo "$top_domain" | cut -c1-38)"..."
  635. fi
  636.  
  637. if [ ${#top_client} -gt 38 ]; then
  638. top_client=$(echo "$top_client" | cut -c1-38)"..."
  639. fi
  640. elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then
  641. ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 40 "color")
  642. else
  643. ads_blocked_bar=$(BarGenerator "$ads_percentage_today" 30 "color")
  644. fi
  645.  
  646. # System uptime
  647. if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then
  648. system_uptime=$(echo "${system_uptime_raw}" | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours"}')
  649. else
  650. system_uptime=$(echo "${system_uptime_raw}" | awk -F'( |,|:)+' '{if ($7=="min") m=$6; else {if ($7~/^day/){if ($9=="min") {d=$6;m=$8} else {d=$6;h=$8;m=$9}} else {h=$6;m=$7}}} {print d+0,"days,",h+0,"hours,",m+0,"minutes"}')
  651. fi
  652.  
  653. # Bar generations
  654. if [ "$1" = "mini" ]; then
  655. cpu_bar=$(BarGenerator "${cpu_percent}" 20)
  656. memory_bar=$(BarGenerator "${memory_percent}" 20)
  657. elif [ "$1" = "tiny" ]; then
  658. cpu_bar=$(BarGenerator "${cpu_percent}" 7)
  659. memory_bar=$(BarGenerator "${memory_percent}" 7)
  660. else
  661. cpu_bar=$(BarGenerator "${cpu_percent}" 10)
  662. memory_bar=$(BarGenerator "${memory_percent}" 10)
  663. fi
  664. }
  665.  
  666. SetStatusMessage() {
  667. # depending on which flags are set, the "message field" shows a different output
  668. # 7 messages are possible (from highest to lowest priority):
  669.  
  670. # - System is hot
  671. # - FTLDNS service is not running
  672. # - Pi-hole's DNS server is off (FTL running, but not providing DNS)
  673. # - Unable to determine Pi-hole blocking status
  674. # - Pi-hole blocking disabled
  675. # - Updates are available
  676. # - Everything is fine
  677.  
  678.  
  679. if [ "${hot_flag}" = true ]; then
  680. # Check if CPU temperature is high
  681. pico_status="${pico_status_hot}"
  682. mini_status="${mini_status_hot} ${blinking_text}${red_text}${temperature}${reset_text}"
  683. tiny_status="${tiny_status_hot} ${blinking_text}${red_text}${temperature}${reset_text}"
  684. full_status="${full_status_hot} ${blinking_text}${red_text}${temperature}${reset_text}"
  685. mega_status="${mega_status_hot} ${blinking_text}${red_text}${temperature}${reset_text}"
  686.  
  687. elif [ "${ftl_down_flag}" = true ]; then
  688. # Check if FTL is down
  689. pico_status=${pico_status_ftl_down}
  690. mini_status=${mini_status_ftl_down}
  691. tiny_status=${tiny_status_ftl_down}
  692. full_status=${full_status_ftl_down}
  693. mega_status=${mega_status_ftl_down}
  694.  
  695. elif [ "${dns_down_flag}" = true ]; then
  696. # Check if DNS is down
  697. pico_status=${pico_status_dns_down}
  698. mini_status=${mini_status_dns_down}
  699. tiny_status=${tiny_status_dns_down}
  700. full_status=${full_status_dns_down}
  701. mega_status=${mega_status_dns_down}
  702.  
  703. elif [ "${blocking_status}" = "unknown" ]; then
  704. # Check if blocking status is unknown
  705. pico_status=${pico_status_unknown}
  706. mini_status=${mini_status_unknown}
  707. tiny_status=${tiny_status_unknown}
  708. full_status=${full_status_unknown}
  709. mega_status=${mega_status_unknown}
  710.  
  711. elif [ "${blocking_status}" = "disabled" ]; then
  712. # Check if blocking status is disabled
  713. pico_status=${pico_status_off}
  714. mini_status=${mini_status_off}
  715. tiny_status=${tiny_status_off}
  716. full_status=${full_status_off}
  717. mega_status=${mega_status_off}
  718.  
  719. elif [ "${out_of_date_flag}" = "true" ] || [ "${padd_out_of_date_flag}" = "true" ]; then
  720. # Check if one of the components of Pi-hole (or PADD itself) is out of date
  721. pico_status=${pico_status_update}
  722. mini_status=${mini_status_update}
  723. tiny_status=${tiny_status_update}
  724. full_status=${full_status_update}
  725. mega_status=${mega_status_update}
  726.  
  727. elif [ "${blocking_status}" = "enabled" ]; then
  728. # if we reach this point and blocking is enabled, everything is fine
  729. pico_status=${pico_status_ok}
  730. mini_status=${mini_status_ok}
  731. tiny_status=${tiny_status_ok}
  732. full_status=${full_status_ok}
  733. mega_status=${mega_status_ok}
  734. fi
  735. }
  736.  
  737. ############################################# PRINTERS #############################################
  738.  
  739. PrintLogo() {
  740. if [ -n "${DOCKER_VERSION}" ]; then
  741. version_info="Docker ${docker_version_heatmap}${DOCKER_VERSION}${reset_text}"
  742. else
  743. version_info="Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}"
  744. fi
  745.  
  746. # Screen size checks
  747. if [ "$1" = "pico" ]; then
  748. printf "%s${clear_line}\n" "p${padd_text} ${pico_status}"
  749. elif [ "$1" = "nano" ]; then
  750. printf "%s${clear_line}\n" "n${padd_text} ${mini_status}"
  751. elif [ "$1" = "micro" ]; then
  752. printf "%s${clear_line}\n${clear_line}\n" "µ${padd_text} ${mini_status}"
  753. elif [ "$1" = "mini" ]; then
  754. printf "%s${clear_line}\n${clear_line}\n" "${padd_text}${dim_text}mini${reset_text} ${mini_status}"
  755. elif [ "$1" = "tiny" ]; then
  756. printf "%s${clear_line}\n" "${padd_text}${dim_text}tiny${reset_text} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}"
  757. printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${tiny_status}${reset_text}"
  758. elif [ "$1" = "slim" ]; then
  759. printf "%s${clear_line}\n${clear_line}\n" "${padd_text}${dim_text}slim${reset_text} ${full_status}"
  760. elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then
  761. printf "%s${clear_line}\n" "${padd_logo_1}"
  762. printf "%s${clear_line}\n" "${padd_logo_2}Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}"
  763. printf "%s${clear_line}\n${clear_line}\n" "${padd_logo_3}PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}"
  764. # normal or not defined
  765. else
  766. printf "%s${clear_line}\n" "${padd_logo_retro_1}"
  767. printf "%s${clear_line}\n" "${padd_logo_retro_2} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}, PADD ${padd_version_heatmap}${padd_version}${reset_text}"
  768. printf "%s${clear_line}\n${clear_line}\n" "${padd_logo_retro_3} ${dns_check_box} DNS ${ftl_check_box} FTL ${mega_status}${reset_text}"
  769. fi
  770. }
  771.  
  772. PrintDashboard() {
  773. if [ -n "${DOCKER_VERSION}" ]; then
  774. version_info="Docker ${docker_version_heatmap}${DOCKER_VERSION}${reset_text}"
  775. else
  776. version_info="Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}"
  777. fi
  778. # Move cursor to (0,0).
  779. printf '\e[H'
  780.  
  781. # adds the y-offset
  782. moveYOffset
  783.  
  784. if [ "$1" = "pico" ]; then
  785. # pico is a screen at least 20x10 (columns x lines)
  786. moveXOffset; printf "%s${clear_line}\n" "p${padd_text} ${pico_status}"
  787. moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ============${reset_text}"
  788. moveXOffset; printf "%s${clear_line}\n" " [${ads_blocked_bar}] ${ads_percentage_today}%"
  789. moveXOffset; printf "%s${clear_line}\n" " ${ads_blocked_today} / ${dns_queries_today}"
  790. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ============${reset_text}"
  791. moveXOffset; printf "%s${clear_line}\n" " Hst: ${pi_hostname}"
  792. moveXOffset; printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}"
  793. moveXOffset; printf "%s${clear_line}\n" " DHCP ${dhcp_check_box} IPv6 ${dhcp_ipv6_check_box}"
  794. moveXOffset; printf "%s${clear_line}\n" "${bold_text}CPU ================${reset_text}"
  795. moveXOffset; printf "%s${clear_line}" " [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%"
  796. elif [ "$1" = "nano" ]; then
  797. # nano is a screen at least 24x12 (columns x lines)
  798. moveXOffset; printf "%s${clear_line}\n" "n${padd_text} ${mini_status}"
  799. moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ================${reset_text}"
  800. moveXOffset; printf "%s${clear_line}\n" " DNS: ${dns_check_box} FTL: ${ftl_check_box}"
  801. moveXOffset; printf "%s${clear_line}\n" " Blk: [${ads_blocked_bar}] ${ads_percentage_today}%"
  802. moveXOffset; printf "%s${clear_line}\n" " Blk: ${ads_blocked_today} / ${dns_queries_today}"
  803. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ================${reset_text}"
  804. moveXOffset; printf "%s${clear_line}\n" " Host: ${pi_hostname}"
  805. moveXOffset; printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}"
  806. moveXOffset; printf "%s${clear_line}\n" " DHCP: ${dhcp_check_box} IPv6: ${dhcp_ipv6_check_box}"
  807. moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =================${reset_text}"
  808. moveXOffset; printf "%s${clear_line}\n" " Up: ${system_uptime}"
  809. moveXOffset; printf "%s${clear_line}" " CPU: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%"
  810. elif [ "$1" = "micro" ]; then
  811. # micro is a screen at least 30x16 (columns x lines)
  812. moveXOffset; printf "%s${clear_line}\n" "µ${padd_text} ${mini_status}"
  813. moveXOffset; printf "%s${clear_line}\n" ""
  814. moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ======================${reset_text}"
  815. moveXOffset; printf "%s${clear_line}\n" " DNS: ${dns_check_box} FTL: ${ftl_check_box}"
  816. moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS ========================${reset_text}"
  817. moveXOffset; printf "%s${clear_line}\n" " Blckng: ${domains_being_blocked} domains"
  818. moveXOffset; printf "%s${clear_line}\n" " Piholed: [${ads_blocked_bar}] ${ads_percentage_today}%"
  819. moveXOffset; printf "%s${clear_line}\n" " Piholed: ${ads_blocked_today} / ${dns_queries_today}"
  820. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ======================${reset_text}"
  821. moveXOffset; printf "%s${clear_line}\n" " Host: ${full_hostname}"
  822. moveXOffset; printf "%s${clear_line}\n" " IP: ${pi_ip4_addr}"
  823. moveXOffset; printf "%s${clear_line}\n" " DHCP: ${dhcp_check_box} IPv6: ${dhcp_ipv6_check_box}"
  824. moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =======================${reset_text}"
  825. moveXOffset; printf "%s${clear_line}\n" " Uptime: ${system_uptime}"
  826. moveXOffset; printf "%s${clear_line}\n" " Load: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%"
  827. moveXOffset; printf "%s${clear_line}" " Memory: [${memory_heatmap}${memory_bar}${reset_text}] ${memory_percent}%"
  828. elif [ "$1" = "mini" ]; then
  829. # mini is a screen at least 40x18 (columns x lines)
  830. moveXOffset; printf "%s${clear_line}\n" "${padd_text}${dim_text}mini${reset_text} ${mini_status}"
  831. moveXOffset; printf "%s${clear_line}\n" ""
  832. moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ================================${reset_text}"
  833. moveXOffset; printf " %-9s${dns_heatmap}%-10s${reset_text} %-5s${ftl_heatmap}%-10s${reset_text}${clear_line}\n" "DNS:" "${dns_status}" "FTL:" "${ftl_status}"
  834. moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS ==================================${reset_text}"
  835. moveXOffset; printf " %-9s%-29s${clear_line}\n" "Blckng:" "${domains_being_blocked} domains"
  836. moveXOffset; printf " %-9s[%-20s] %-5s${clear_line}\n" "Piholed:" "${ads_blocked_bar}" "${ads_percentage_today}%"
  837. moveXOffset; printf " %-9s%-29s${clear_line}\n" "Piholed:" "${ads_blocked_today} out of ${dns_queries_today}"
  838. moveXOffset; printf " %-9s%-29s${clear_line}\n" "Latest:" "${latest_blocked}"
  839. if [ "${DHCP_ACTIVE}" != "true" ]; then
  840. moveXOffset; printf " %-9s%-29s${clear_line}\n" "Top Ad:" "${top_blocked}"
  841. fi
  842. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ================================${reset_text}"
  843. moveXOffset; printf " %-9s%-16s%-5s%-9s${clear_line}\n" "Host:" "${full_hostname}" "DNS:" "${dns_information}"
  844. moveXOffset; printf " %-9s%s${clear_line}\n" "IP:" "${pi_ip4_addr} (${iface_name})"
  845. moveXOffset; printf " %-9s%-4s%-12s%-4s%-5s${clear_line}\n" "Traffic:" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}"
  846. if [ "${DHCP_ACTIVE}" = "true" ]; then
  847. moveXOffset; printf " %-9s${dhcp_heatmap}%-10s${reset_text} %-9s${dhcp_ipv6_heatmap}%-10s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status}
  848. fi
  849. moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =================================${reset_text}"
  850. moveXOffset; printf " %-9s%-29s${clear_line}\n" "Uptime:" "${system_uptime}"
  851. moveXOffset; printf "%s${clear_line}\n" " Load: [${cpu_load_1_heatmap}${cpu_bar}${reset_text}] ${cpu_percent}%"
  852. moveXOffset; printf "%s${clear_line}" " Memory: [${memory_heatmap}${memory_bar}${reset_text}] ${memory_percent}%"
  853. elif [ "$1" = "tiny" ]; then
  854. # tiny is a screen at least 53x20 (columns x lines)
  855. moveXOffset; printf "%s${clear_line}\n" "${padd_text}${dim_text}tiny${reset_text} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}"
  856. moveXOffset; printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${tiny_status}${reset_text}"
  857. moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ============================================${reset_text}"
  858. moveXOffset; printf " %-10s${dns_heatmap}%-16s${reset_text} %-8s${ftl_heatmap}%-10s${reset_text}${clear_line}\n" "DNS:" "${dns_status}" "FTL:" "${ftl_status}"
  859. moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS ==============================================${reset_text}"
  860. moveXOffset; printf " %-10s%-29s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains"
  861. moveXOffset; printf " %-10s[%-30s] %-5s${clear_line}\n" "Pi-holed:" "${ads_blocked_bar}" "${ads_percentage_today}%"
  862. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Pi-holed:" "${ads_blocked_today} out of ${dns_queries_today}"
  863. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}"
  864. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}"
  865. if [ "${DHCP_ACTIVE}" != "true" ]; then
  866. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}"
  867. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}"
  868. fi
  869. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ============================================${reset_text}"
  870. moveXOffset; printf " %-10s%-16s %-8s%-16s${clear_line}\n" "Hostname:" "${full_hostname}" "IP: " "${pi_ip4_addr}"
  871. moveXOffset; printf " %-10s%-16s %-4s%-7s %-4s%-5s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}"
  872. moveXOffset; printf " %-10s%-16s %-8s${dnssec_heatmap}%-16s${reset_text}${clear_line}\n" "DNS:" "${dns_information}" "DNSSEC:" "${dnssec_status}"
  873. if [ "${DHCP_ACTIVE}" = "true" ]; then
  874. moveXOffset; printf " %-10s${dhcp_heatmap}%-16s${reset_text} %-8s${dhcp_ipv6_heatmap}%-10s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status}
  875. moveXOffset; printf "%s${clear_line}\n" "${dhcp_info}"
  876. fi
  877. moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =============================================${reset_text}"
  878. moveXOffset; printf " %-10s%-29s${clear_line}\n" "Uptime:" "${system_uptime}"
  879. moveXOffset; printf " %-10s${temp_heatmap}%-17s${reset_text} %-8s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-4s${reset_text}${clear_line}\n" "CPU Temp:" "${temperature}" "Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}"
  880. moveXOffset; printf " %-10s[${memory_heatmap}%-7s${reset_text}] %-6s %-8s[${cpu_load_1_heatmap}%-7s${reset_text}] %-5s${clear_line}" "Memory:" "${memory_bar}" "${memory_percent}%" "CPU:" "${cpu_bar}" "${cpu_percent}%"
  881. elif [ "$1" = "regular" ] || [ "$1" = "slim" ]; then
  882. # slim is a screen with at least 60 columns and exactly 21 lines
  883. # regular is a screen at least 60x22 (columns x lines)
  884. if [ "$1" = "slim" ]; then
  885. moveXOffset; printf "%s${clear_line}\n" "${padd_text}${dim_text}slim${reset_text} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}"
  886. moveXOffset; printf "%s${clear_line}\n" " PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}"
  887. moveXOffset; printf "%s${clear_line}\n" ""
  888. else
  889. moveXOffset; printf "%s${clear_line}\n" "${padd_logo_1}"
  890. moveXOffset; printf "%s${clear_line}\n" "${padd_logo_2}Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text}, Web ${web_version_heatmap}${WEB_VERSION}${reset_text}, FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text}"
  891. moveXOffset; printf "%s${clear_line}\n" "${padd_logo_3}PADD ${padd_version_heatmap}${padd_version}${reset_text} ${full_status}${reset_text}"
  892. moveXOffset; printf "%s${clear_line}\n" ""
  893. fi
  894. moveXOffset; printf "%s${clear_line}\n" "${bold_text}PI-HOLE ===================================================${reset_text}"
  895. moveXOffset; printf " %-10s${dns_heatmap}%-19s${reset_text} %-10s${ftl_heatmap}%-19s${reset_text}${clear_line}\n" "DNS:" "${dns_status}" "FTL:" "${ftl_status}"
  896. moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS =====================================================${reset_text}"
  897. moveXOffset; printf " %-10s%-49s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains"
  898. moveXOffset; printf " %-10s[%-40s] %-5s${clear_line}\n" "Pi-holed:" "${ads_blocked_bar}" "${ads_percentage_today}%"
  899. moveXOffset; printf " %-10s%-49s${clear_line}\n" "Pi-holed:" "${ads_blocked_today} out of ${dns_queries_today} queries"
  900. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}"
  901. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}"
  902. if [ "${DHCP_ACTIVE}" != "true" ]; then
  903. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}"
  904. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}"
  905. fi
  906. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ===================================================${reset_text}"
  907. moveXOffset; printf " %-10s%-15s %-4s%-17s%-6s%s${clear_line}\n" "Hostname:" "${full_hostname}" "IP:" "${pi_ip4_addr}" "IPv6:" "${ipv6_check_box}"
  908. moveXOffset; printf " %-10s%-15s %-4s%-17s%-4s%s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" "RX:" "${rx_bytes}"
  909. moveXOffset; printf " %-10s%-15s %-10s${dnssec_heatmap}%-19s${reset_text}${clear_line}\n" "DNS:" "${dns_information}" "DNSSEC:" "${dnssec_status}"
  910. if [ "${DHCP_ACTIVE}" = "true" ]; then
  911. moveXOffset; printf " %-10s${dhcp_heatmap}%-15s${reset_text} %-10s${dhcp_ipv6_heatmap}%-19s${reset_text}${clear_line}\n" "DHCP:" "${dhcp_status}" "IPv6:" ${dhcp_ipv6_status}
  912. moveXOffset; printf "%s${clear_line}\n" "${dhcp_info}"
  913. fi
  914. moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM ====================================================${reset_text}"
  915. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Uptime:" "${system_uptime}"
  916. moveXOffset; printf " %-10s${temp_heatmap}%-21s${reset_text}%-10s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-4s${reset_text}${clear_line}\n" "CPU Temp:" "${temperature}" "CPU Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}"
  917. moveXOffset; printf " %-10s[${memory_heatmap}%-10s${reset_text}] %-6s %-10s[${cpu_load_1_heatmap}%-10s${reset_text}] %-5s${clear_line}" "Memory:" "${memory_bar}" "${memory_percent}%" "CPU Load:" "${cpu_bar}" "${cpu_percent}%"
  918. else # ${padd_size} = mega
  919.  
  920.  
  921. # mega is a screen with at least 80 columns and 26 lines
  922. moveXOffset; printf "%s${clear_line}\n" "${padd_logo_retro_1}"
  923. moveXOffset; printf "%s${clear_line}\n" "${padd_logo_retro_2} Pi-hole® ${core_version_heatmap}${CORE_VERSION}${reset_text} Web ${web_version_heatmap}${WEB_VERSION}${reset_text} FTL ${ftl_version_heatmap}${FTL_VERSION}${reset_text} PADD ${padd_version_heatmap}${padd_version}${reset_text} Unbound ${green_text}v${unbound}${reset_text}"
  924. moveXOffset; printf "%s${clear_line}\n" "${padd_logo_retro_3} ${pihole_check_box} Core ${ftl_check_box} FTL ${mega_status}${reset_text} ${check_box_info} "$(date +%d/%m/%y)", "$(date +%R)" ${reset_text}"
  925. moveXOffset; printf "%s${clear_line}\n" ""
  926. moveXOffset; printf "%s${clear_line}\n" "${bold_text}STATS==============================================================================================${reset_text}"
  927. moveXOffset; printf " %-10s%-29s %-10s[%-40s] %-5s${clear_line}\n" "Blocking:" "${domains_being_blocked} domains" "Piholed:" "${ads_blocked_bar}" "${ads_percentage_today}%"
  928. moveXOffset; printf " %-10s%-40s%-29s${clear_line}\n" "Clients:" "${clients}" " ${ads_blocked_today} out of ${dns_queries_today} queries"
  929. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Latest:" "${latest_blocked}"
  930. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Ad:" "${top_blocked}"
  931. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Dmn:" "${top_domain}"
  932. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Top Clnt:" "${top_client}"
  933. moveXOffset; printf "%s${clear_line}\n" "${bold_text}FTL ================================================================================================${reset_text}"
  934. moveXOffset; printf " %-10s%-9s %-10s%-9s %-10s%-9s${clear_line}\n" "PID:" "${ftlPID}" "CPU Use:" "${ftl_cpu}%" "Mem. Use:" "${ftl_mem_percentage}%"
  935. moveXOffset; printf " %-10s%-69s${clear_line}\n" "DNSCache:" "${cache_inserts} insertions, ${cache_deletes} deletions, ${cache_size} total entries"
  936. moveXOffset; printf "%s${clear_line}\n" "${bold_text}NETWORK ============================================================================================${reset_text}"
  937. #moveXOffset; printf " %-10s%-19s${clear_line}\n" "Hostname:" "${full_hostname}"
  938. moveXOffset; printf " %-10s%-19s %-10s%-9s %-4s%-9s${clear_line}\n" "Hostname:" "${full_hostname}" "IP:" "${ext_ip}"
  939. moveXOffset; printf " %-10s%-19s %-10s%-9s %-4s%-9s${clear_line}\n" "Interfce:" "${iface_name}" "TX:" "${tx_bytes}" #"RX:" "${rx_bytes}"
  940. moveXOffset; printf " %-10s%-19s %-10s%-29s${clear_line}\n" "IPv4:" "${pi_ip4_addr}" "RX:" "${rx_bytes}"
  941.  
  942. moveXOffset; printf "%s${clear_line}\n" "${bold_text}UNBOUND ============================================================================================${reset_text}"
  943. moveXOffset; printf " %-10s%-19s%-10s%-9s\e[0K\\n" "Total: " "$(sudo unbound-control stats_noreset | awk -F '=' '/total.num.queries=/ {printf $NF}') queries" " Reply:" " $(sudo unbound-control stats_noreset | awk -F '=' '/total.recursion.time.avg/ {printf "%.3f\n", $NF}') seconds"
  944.  
  945. moveXOffset; printf " %-10s%-19s%-10s%-9s\e[0K\\n" "Cached:" "$(sudo unbound-control stats_noreset | awk -F '=' '$1 == "total.num.queries" {queries=$NF} $1 == "total.num.cachehits" {hits=$NF}END{printf "%d, %.2f\n", hits, hits/queries*100"%"}')% " " Updted: " " $(cat /var/lib/unbound/root.hints | awk '/last update/ {printf ($4" "$5" "$6)}') "
  946. moveXOffset; printf "%s${clear_line}\n" "${bold_text}DNS ==========================DHCP==================================================================${reset_text}"
  947. moveXOffset; printf " %-10s%-19s %-6s${dhcp_heatmap}%-19s${reset_text}${clear_line}\n" "Servers:" "${dns_information}" "DHCP:" "${dhcp_status}"
  948. moveXOffset; printf " %-10s${dnssec_heatmap}%-19s${reset_text} %-10s${conditional_forwarding_heatmap}%-9s${reset_text}${clear_line}\n" "DNSSEC:" "${dnssec_status}" "IPv6 Spt:" "${dhcp_ipv6_status}"
  949. moveXOffset; printf " %-10s${conditional_forwarding_heatmap}%-19s${reset_text}%s${clear_line}\n" "CdFwding:" "${conditional_forwarding_status}" "${dhcp_info}"
  950.  
  951. moveXOffset; printf "%s${clear_line}\n" "${bold_text}INFO ===============================================================================================${reset_text}"
  952. # Boot and log
  953. moveXOffset; printf " %-10s%-19s%-6s%-9s\e[0K\\n" "PS: " "${run_process}" " Log:" " ${log_size}B (${log_perc})"
  954. # Disk Usage
  955. moveXOffset; printf " %-10s%-19s%-6s%-9s\e[0K\\n" "Disk: " "${disk_total}GB" " Used:" " ${disk_used}GB (${disk_perc})"
  956.  
  957.  
  958.  
  959. moveXOffset; printf "%s${clear_line}\n" "${bold_text}SYSTEM =============================================================================================${reset_text}"
  960. moveXOffset; printf " %-10s%-39s${clear_line}\n" "Device:" "${sys_model}"
  961. moveXOffset; printf " %-10s%-39s %-10s[${memory_heatmap}%-10s${reset_text}] %-6s${clear_line}\n" "Uptime:" "${system_uptime}" "RAM use $(free -m | awk 'NR==2{printf "%s", $3 }') MB of ${ram_total} GB: " "${memory_bar}" "${memory_percent}% "
  962. moveXOffset; printf " %-10s${temp_heatmap}%-9s${reset_text}%-6s${cpu_load_1_heatmap}%-4s${reset_text}, ${cpu_load_5_heatmap}%-4s${reset_text}, ${cpu_load_15_heatmap}%-13s${reset_text} %-6s[${memory_heatmap}%-10s${reset_text}] %-6s${clear_line}" "Temp:" "${temperature}" "Load:" "${cpu_load_1}" "${cpu_load_5}" "${cpu_load_15}" "CPU $(awk 'length==6{printf("%.0f MHz\n", $0/10^3); next} length==7{printf("%.1f GHz\n", $0/10^6)}' /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) ${cpu_freq}: "$(printf '%s')" " "${cpu_bar}" "${cpu_percent}%"
  963. fi
  964. # Clear to end of screen (below the drawn dashboard)
  965. # https://vt100.net/docs/vt510-rm/ED.html
  966. printf '\e[0J'
  967. }
  968.  
  969. ############################################# HELPERS ##############################################
  970.  
  971. # Provides a color based on a provided percentage
  972. # takes in one or two parameters
  973. HeatmapGenerator () {
  974. # if one number is provided, just use that percentage to figure out the colors
  975. if [ -z "$2" ]; then
  976. load=$(printf "%.0f" "$1")
  977. # if two numbers are provided, do some math to make a percentage to figure out the colors
  978. else
  979. load=$(printf "%.0f" "$(echo "$1 $2" | awk '{print ($1 / $2) * 100}')")
  980. fi
  981.  
  982. # Color logic
  983. # |<- green ->| yellow | red ->
  984. # 0 5 10 15 20 25 30 35 40 45 50 55 60 65 70 75 80 85 90 95 100
  985. if [ "${load}" -lt 75 ]; then
  986. out=${green_text}
  987. elif [ "${load}" -lt 90 ]; then
  988. out=${yellow_text}
  989. else
  990. out=${red_text}
  991. fi
  992.  
  993. echo "$out"
  994. }
  995.  
  996. # Provides a "bar graph"
  997. # takes in two or three parameters
  998. # $1: percentage filled
  999. # $2: max length of the bar
  1000. # $3: colored flag, if "color" backfill with color
  1001. BarGenerator() {
  1002. # number of filled in cells in the bar
  1003. barNumber=$(printf %.f "$(echo "$1 $2" | awk '{print ($1 / 100) * $2}')")
  1004. frontFill=$(for i in $(seq "$barNumber"); do printf "%b" "■"; done)
  1005.  
  1006. # remaining "unfilled" cells in the bar
  1007. backfillNumber=$(($2-barNumber))
  1008.  
  1009. # if the filled in cells is less than the max length of the bar, fill it
  1010. if [ "$barNumber" -lt "$2" ]; then
  1011. # if the bar should be colored
  1012. if [ "$3" = "color" ]; then
  1013. # fill the rest in color
  1014. backFill=$(for i in $(seq $backfillNumber); do printf "%b" "■"; done)
  1015. out="${red_text}${frontFill}${green_text}${backFill}${reset_text}"
  1016. # else, it shouldn't be colored in
  1017. else
  1018. # fill the rest with "space"
  1019. backFill=$(for i in $(seq $backfillNumber); do printf "%b" "·"; done)
  1020. out="${frontFill}${reset_text}${backFill}"
  1021. fi
  1022. # else, fill it all the way
  1023. else
  1024. out=$(for i in $(seq "$2"); do printf "%b" "■"; done)
  1025. fi
  1026.  
  1027. echo "$out"
  1028. }
  1029.  
  1030. # Checks the size of the screen and sets the value of $padd_size
  1031. SizeChecker(){
  1032. # adding a tiny delay here to to give the kernel a bit time to
  1033. # report new sizes correctly after a terminal resize
  1034. # this reduces "flickering" of GenerateSizeDependendOutput() items
  1035. # after a terminal re-size
  1036. sleep 0.1
  1037. console_width=$(tput cols)
  1038. console_height=$(tput lines)
  1039.  
  1040. # Mega
  1041. if [ "$console_width" -ge "80" ] && [ "$console_height" -ge "26" ]; then
  1042. padd_size="mega"
  1043. width=80
  1044. height=26
  1045. # Below Mega. Gives you Regular.
  1046. elif [ "$console_width" -ge "60" ] && [ "$console_height" -ge "22" ]; then
  1047. padd_size="regular"
  1048. width=60
  1049. height=22
  1050. # Below Regular. Gives you Slim.
  1051. elif [ "$console_width" -ge "60" ] && [ "$console_height" -ge "21" ]; then
  1052. padd_size="slim"
  1053. width=60
  1054. height=21
  1055. # Below Slim. Gives you Tiny.
  1056. elif [ "$console_width" -ge "53" ] && [ "$console_height" -ge "20" ]; then
  1057. padd_size="tiny"
  1058. width=53
  1059. height=20
  1060. # Below Tiny. Gives you Mini.
  1061. elif [ "$console_width" -ge "40" ] && [ "$console_height" -ge "18" ]; then
  1062. padd_size="mini"
  1063. width=40
  1064. height=18
  1065. # Below Mini. Gives you Micro.
  1066. elif [ "$console_width" -ge "30" ] && [ "$console_height" -ge "16" ]; then
  1067. padd_size="micro"
  1068. width=30
  1069. height=16
  1070. # Below Micro, Gives you Nano.
  1071. elif [ "$console_width" -ge "24" ] && [ "$console_height" -ge "12" ]; then
  1072. padd_size="nano"
  1073. width=24
  1074. height=12
  1075. # Below Nano. Gives you Pico.
  1076. elif [ "$console_width" -ge "20" ] && [ "$console_height" -ge "10" ]; then
  1077. padd_size="pico"
  1078. width=20
  1079. height=10
  1080. # Below Pico. Gives you nothing...
  1081. else
  1082. # Nothing is this small, sorry
  1083. printf "%b" "${check_box_bad} Error!\n PADD isn't\n for ants!\n"
  1084. exit 1
  1085. fi
  1086.  
  1087. # Center the output (default position)
  1088. xOffset="$(( (console_width - width) / 2 ))"
  1089. yOffset="$(( (console_height - height) / 2 ))"
  1090.  
  1091. # If the user sets an offset option, use it.
  1092. if [ -n "$xOffOrig" ]; then
  1093. xOffset=$xOffOrig
  1094.  
  1095. # Limit the offset to avoid breaks
  1096. xMaxOffset=$((console_width - width))
  1097. if [ "$xOffset" -gt "$xMaxOffset" ]; then
  1098. xOffset="$xMaxOffset"
  1099. fi
  1100. fi
  1101. if [ -n "$yOffOrig" ]; then
  1102. yOffset=$yOffOrig
  1103.  
  1104. # Limit the offset to avoid breaks
  1105. yMaxOffset=$((console_height - height))
  1106. if [ "$yOffset" -gt "$yMaxOffset" ]; then
  1107. yOffset="$yMaxOffset"
  1108. fi
  1109. fi
  1110. }
  1111.  
  1112. # converts a given version string e.g. v3.7.1 to 3007001000 to allow for easier comparison of multi digit version numbers
  1113. # credits https://apple.stackexchange.com/a/123408
  1114. VersionConverter() {
  1115. echo "$@" | tr -d '[:alpha:]' | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }';
  1116. }
  1117.  
  1118. # get the Telnet API Port FTL is using by parsing `pihole-FTL.conf`
  1119. # same implementation as https://github.com/pi-hole/pi-hole/pull/4945
  1120. getFTLAPIPort(){
  1121. local FTLCONFFILE="/etc/pihole/pihole-FTL.conf"
  1122. local DEFAULT_FTL_PORT=4711
  1123. local ftl_api_port
  1124.  
  1125. if [ -s "$FTLCONFFILE" ]; then
  1126. # if FTLPORT is not set in pihole-FTL.conf, use the default port
  1127. ftl_api_port="$({ grep '^FTLPORT=' "${FTLCONFFILE}" || echo "${DEFAULT_FTL_PORT}"; } | cut -d'=' -f2-)"
  1128. # Exploit prevention: set the port to the default port if there is malicious (non-numeric)
  1129. # content set in pihole-FTL.conf
  1130. expr "${ftl_api_port}" : "[^[:digit:]]" > /dev/null && ftl_api_port="${DEFAULT_FTL_PORT}"
  1131. else
  1132. # if there is no pihole-FTL.conf, use the default port
  1133. ftl_api_port="${DEFAULT_FTL_PORT}"
  1134. fi
  1135.  
  1136. echo "${ftl_api_port}"
  1137.  
  1138. }
  1139.  
  1140. # returns FTL's PID based on the content of the pihole-FTL.pid file
  1141. # honor PIDFILE setting in `pihole-FTL.conf`
  1142. getFTLPID() {
  1143. local FTLCONFFILE="/etc/pihole/pihole-FTL.conf"
  1144. local DEFAULT_PID_FILE="/run/pihole-FTL.pid"
  1145. local FTL_PID_FILE
  1146. local FTL_PID
  1147.  
  1148. if [ -s "${FTLCONFFILE}" ]; then
  1149. # if PIDFILE is not set in pihole-FTL.conf, use the default path
  1150. FTL_PID_FILE="$({ grep '^PIDFILE=' "${FTLCONFFILE}" || echo "${DEFAULT_PID_FILE}"; } | cut -d'=' -f2-)"
  1151. else
  1152. # if there is no pihole-FTL.conf, use the default path
  1153. FTL_PID_FILE="${DEFAULT_PID_FILE}"
  1154. fi
  1155.  
  1156. if [ -s "${FTL_PID_FILE}" ]; then
  1157. # -s: FILE exists and has a size greater than zero
  1158. FTL_PID="$(cat "${FTL_PID_FILE}")"
  1159. # Exploit prevention: unset the variable if there is malicious content
  1160. # Verify that the value read from the file is numeric
  1161. expr "${FTL_PID}" : "[^[:digit:]]" > /dev/null && unset FTL_PID
  1162. fi
  1163.  
  1164. # If FTL is not running, or the PID file contains malicious stuff, substitute
  1165. # negative PID to signal this
  1166. FTL_PID=${FTL_PID:=-1}
  1167. echo "${FTL_PID}"
  1168. }
  1169.  
  1170.  
  1171. moveYOffset(){
  1172. # moves the cursor yOffset-times down
  1173. # https://vt100.net/docs/vt510-rm/CUD.html
  1174. # this needs to be guarded, because if the amount is 0, it is adjusted to 1
  1175. # https://terminalguide.namepad.de/seq/csi_cb/
  1176.  
  1177. if [ "${yOffset}" -gt 0 ]; then
  1178. printf '\e[%sB' "${yOffset}"
  1179. fi
  1180. }
  1181.  
  1182. moveXOffset(){
  1183. # moves the cursor xOffset-times to the right
  1184. # https://vt100.net/docs/vt510-rm/CUF.html
  1185. # this needs to be guarded, because if the amount is 0, it is adjusted to 1
  1186. # https://terminalguide.namepad.de/seq/csi_cb/
  1187.  
  1188. if [ "${xOffset}" -gt 0 ]; then
  1189. printf '\e[%sC' "${xOffset}"
  1190. fi
  1191. }
  1192.  
  1193. # Remove undesired strings from sys_model variable - used in GetSystemInformation() function
  1194. filterModel() {
  1195. FILTERLIST="To be filled by O.E.M.|Not Applicable|System Product Name|System Version|Undefined|Default string|Not Specified|Type1ProductConfigId|INVALID|All Series|�"
  1196.  
  1197. # Description:
  1198. # `-v` : set $FILTERLIST into a variable called `list`
  1199. # `gsub()` : replace all list items (ignoring case) with an empty string, deleting them
  1200. # `{$1=$1}1`: remove all extra spaces. The last "1" evaluates as true, printing the result
  1201. echo "$1" | awk -v list="$FILTERLIST" '{IGNORECASE=1; gsub(list,"")}; {$1=$1}1'
  1202. }
  1203.  
  1204. # Truncates a given string and appends three '...'
  1205. # takes two parameters
  1206. # $1: string to truncate
  1207. # $2: max length of the string
  1208. truncateString() {
  1209. local truncatedString length shorted
  1210.  
  1211. length=${#1}
  1212. shorted=$(($2-3)) # shorten max allowed length by 3 to make room for the dots
  1213. if [ "${length}" -gt "$2" ]; then
  1214. # if length of the string is larger then the specified max length
  1215. # cut every char from the string exceeding length $shorted and add three dots
  1216. truncatedString=$(echo "$1" | cut -c1-$shorted)"..."
  1217. echo "${truncatedString}"
  1218. else
  1219. echo "$1"
  1220. fi
  1221. }
  1222.  
  1223.  
  1224.  
  1225. ########################################## MAIN FUNCTIONS ##########################################
  1226.  
  1227. OutputJSON() {
  1228. GetSummaryInformation
  1229. echo "{\"domains_being_blocked\":${domains_being_blocked_raw},\"dns_queries_today\":${dns_queries_today_raw},\"ads_blocked_today\":${ads_blocked_today_raw},\"ads_percentage_today\":${ads_percentage_today_raw},\"clients\": ${clients}}"
  1230. }
  1231.  
  1232. StartupRoutine(){
  1233. # Get config variables
  1234. . /etc/pihole/setupVars.conf
  1235.  
  1236. # Clear the screen and move cursor to (0,0).
  1237. # This mimics the 'clear' command.
  1238. # https://vt100.net/docs/vt510-rm/ED.html
  1239. # https://vt100.net/docs/vt510-rm/CUP.html
  1240. # E3 extension `\e[3J` to clear the scrollback buffer see 'man clear'
  1241. printf '\e[H\e[2J\e[3J'
  1242.  
  1243. # adds the y-offset
  1244. moveYOffset
  1245.  
  1246. # Get versions information
  1247. . /etc/pihole/versions
  1248.  
  1249. if [ "$1" = "pico" ] || [ "$1" = "nano" ] || [ "$1" = "micro" ]; then
  1250. moveXOffset; PrintLogo "$1"
  1251. moveXOffset; printf "%b" "START-UP ===========\n"
  1252.  
  1253. moveXOffset; printf "%b" " [■·········] 10%\r"
  1254.  
  1255. # Check for updates
  1256. moveXOffset; printf "%b" " [■■········] 20%\r"
  1257. moveXOffset; printf "%b" " [■■■·······] 30%\r"
  1258.  
  1259. # Get our information for the first time
  1260. moveXOffset; printf "%b" " [■■■■······] 40%\r"
  1261. GetSystemInformation
  1262. moveXOffset; printf "%b" " [■■■■■·····] 50%\r"
  1263. GetSummaryInformation
  1264. moveXOffset; printf "%b" " [■■■■■■····] 60%\r"
  1265. GetPiholeInformation
  1266. moveXOffset; printf "%b" " [■■■■■■■···] 70%\r"
  1267. GetNetworkInformation
  1268. moveXOffset; printf "%b" " [■■■■■■■■··] 80%\r"
  1269. GetVersionInformation
  1270. moveXOffset; printf "%b" " [■■■■■■■■■·] 90%\r"
  1271. GetPADDInformation
  1272. moveXOffset; printf "%b" " [■■■■■■■■■■] 100%\n"
  1273.  
  1274. elif [ "$1" = "mini" ]; then
  1275. moveXOffset; PrintLogo "$1"
  1276. moveXOffset; echo "START UP ====================="
  1277.  
  1278. # Get our information for the first time
  1279. moveXOffset; echo "- Gathering system info."
  1280. GetSystemInformation
  1281. moveXOffset; echo "- Gathering Pi-hole info."
  1282. GetPiholeInformation
  1283. GetSummaryInformation
  1284. moveXOffset; echo "- Gathering network info."
  1285. GetNetworkInformation
  1286. moveXOffset; echo "- Gathering version info."
  1287. GetVersionInformation
  1288. GetPADDInformation
  1289. if [ -n "${DOCKER_VERSION}" ]; then
  1290. moveXOffset; echo " - Docker Tag ${DOCKER_VERSION}"
  1291. else
  1292. moveXOffset; echo " - Core $CORE_VERSION, Web $WEB_VERSION"
  1293. moveXOffset; echo " - FTL $FTL_VERSION, PADD $padd_version"
  1294. fi
  1295.  
  1296. else
  1297. moveXOffset; printf "%b" "${padd_logo_retro_1}\n"
  1298. moveXOffset; printf "%b" "${padd_logo_retro_2}Pi-hole® Ad Detection Display\n"
  1299. moveXOffset; printf "%b" "${padd_logo_retro_3}A client for Pi-hole\n\n"
  1300. if [ "$1" = "tiny" ]; then
  1301. moveXOffset; echo "START UP ============================================"
  1302. else
  1303. moveXOffset; echo "START UP ==================================================="
  1304. fi
  1305.  
  1306. # Get our information for the first time
  1307. moveXOffset; echo "- Gathering system information..."
  1308. GetSystemInformation
  1309. moveXOffset; echo "- Gathering Pi-hole information..."
  1310. GetSummaryInformation
  1311. GetPiholeInformation
  1312. moveXOffset; echo "- Gathering network information..."
  1313. GetNetworkInformation
  1314. moveXOffset; echo "- Gathering version information..."
  1315. GetVersionInformation
  1316. GetPADDInformation
  1317. if [ -n "${DOCKER_VERSION}" ]; then
  1318. moveXOffset; echo " - Docker Tag ${DOCKER_VERSION}"
  1319. else
  1320. moveXOffset; echo " - Pi-hole Core $CORE_VERSION"
  1321. moveXOffset; echo " - Web Admin $WEB_VERSION"
  1322. moveXOffset; echo " - FTL $FTL_VERSION"
  1323. moveXOffset; echo " - PADD $padd_version"
  1324. fi
  1325. fi
  1326.  
  1327. moveXOffset; printf "%s" "- Starting in "
  1328. for i in 3 2 1
  1329. do
  1330. printf "%s..." "$i"
  1331. sleep 1
  1332. done
  1333. }
  1334.  
  1335. NormalPADD() {
  1336.  
  1337. # Trap the window resize signal (handle window resize events)
  1338. trap 'TerminalResize' WINCH
  1339.  
  1340. while true; do
  1341.  
  1342. # Generate output that depends on the terminal size
  1343. # e.g. Heatmap and barchart
  1344. GenerateSizeDependendOutput ${padd_size}
  1345.  
  1346. # Sets the message displayed in the "status field" depending on the set flags
  1347. SetStatusMessage
  1348.  
  1349. # Output everything to the screen
  1350. PrintDashboard ${padd_size}
  1351.  
  1352. # Sleep for 5 seconds
  1353. # sending sleep in the background and wait for it
  1354. # this way the TerminalResize trap can kill the sleep
  1355. # and force a instant re-draw of the dashboard
  1356. # https://stackoverflow.com/questions/32041674/linux-how-to-kill-sleep
  1357. #
  1358. # saving the PID of the background sleep process to kill it on exit and resize
  1359. sleep 5 &
  1360. sleepPID=$!
  1361. wait $!
  1362.  
  1363. # Start getting our information for next round
  1364. now=$(date +%s)
  1365.  
  1366. # Get uptime, CPU load, temp, etc. every 5 seconds
  1367. if [ $((now - LastCheckSystemInformation)) -ge 5 ]; then
  1368. . /etc/pihole/setupVars.conf
  1369. GetSystemInformation
  1370. LastCheckSystemInformation="${now}"
  1371. fi
  1372.  
  1373. # Get cache info, last ad domain, blocking percentage, etc. every 5 seconds
  1374. if [ $((now - LastCheckSummaryInformation)) -ge 5 ]; then
  1375. GetSummaryInformation
  1376. LastCheckSummaryInformation="${now}"
  1377. fi
  1378.  
  1379. # Get FTL status every 5 seconds
  1380. if [ $((now - LastCheckPiholeInformation)) -ge 5 ]; then
  1381. GetPiholeInformation
  1382. LastCheckPiholeInformation="${now}"
  1383. fi
  1384.  
  1385. # Get IPv4 address, DNS servers, DNSSEC, hostname, DHCP status, interface traffic, etc. every 30 seconds
  1386. if [ $((now - LastCheckNetworkInformation)) -ge 30 ]; then
  1387. GetNetworkInformation
  1388. LastCheckNetworkInformation="${now}"
  1389. fi
  1390.  
  1391. # Get Pi-hole components version information every 30 seconds
  1392. if [ $((now - LastCheckVersionInformation)) -ge 30 ]; then
  1393. . /etc/pihole/versions
  1394. GetVersionInformation
  1395. LastCheckVersionInformation="${now}"
  1396. fi
  1397.  
  1398. # Get PADD version information every 24hours
  1399. if [ $((now - LastCheckPADDInformation)) -ge 86400 ]; then
  1400. GetPADDInformation
  1401. LastCheckPADDInformation="${now}"
  1402. fi
  1403.  
  1404. done
  1405. }
  1406.  
  1407. DisplayHelp() {
  1408. cat << EOM
  1409.  
  1410. ::: PADD displays stats about your Pi-hole!
  1411. :::
  1412. ::: Note: If no option is passed, then stats are displayed on screen, updated every 5 seconds
  1413. :::
  1414. ::: Options:
  1415. ::: -xoff [num] set the x-offset, reference is the upper left corner, disables auto-centering
  1416. ::: -yoff [num] set the y-offset, reference is the upper left corner, disables auto-centering
  1417. ::: -j, --json output stats as JSON formatted string and exit
  1418. ::: -h, --help display this help text
  1419.  
  1420. EOM
  1421. exit 0
  1422. }
  1423.  
  1424. CleanExit(){
  1425. # save the return code of the script
  1426. err=$?
  1427. #clear the line
  1428. printf '\e[0K\n'
  1429.  
  1430. # Show the cursor
  1431. # https://vt100.net/docs/vt510-rm/DECTCEM.html
  1432. printf '\e[?25h'
  1433.  
  1434. # if background sleep is running, kill it
  1435. # http://mywiki.wooledge.org/SignalTrap#When_is_the_signal_handled.3F
  1436. kill $sleepPID > /dev/null 2>&1
  1437.  
  1438. exit $err # exit the script with saved $?
  1439. }
  1440.  
  1441. TerminalResize(){
  1442. # if a terminal resize is trapped, check the new terminal size and
  1443. # kill the sleep function within NormalPADD() to trigger redrawing
  1444. # of the Dashboard
  1445. SizeChecker
  1446.  
  1447. # Clear the screen and move cursor to (0,0).
  1448. # This mimics the 'clear' command.
  1449. # https://vt100.net/docs/vt510-rm/ED.html
  1450. # https://vt100.net/docs/vt510-rm/CUP.html
  1451. # E3 extension `\e[3J` to clear the scrollback buffer (see 'man clear')
  1452.  
  1453. printf '\e[H\e[2J\e[3J'
  1454.  
  1455. kill $sleepPID > /dev/null 2>&1
  1456. }
  1457.  
  1458. main(){
  1459. # Hiding the cursor.
  1460. # https://vt100.net/docs/vt510-rm/DECTCEM.html
  1461. printf '\e[?25l'
  1462.  
  1463. # Trap on exit
  1464. trap 'CleanExit' INT TERM EXIT
  1465.  
  1466. # If setupVars.conf is not present, then PADD is not running on a Pi-hole
  1467. # and we are not able to start as StartupRoutine() will fail below
  1468. if [ ! -f /etc/pihole/setupVars.conf ]; then
  1469. printf "%b" "${check_box_bad} Error!\n PADD only works in conjunction with Pi-hole!\n"
  1470. exit 1
  1471. fi
  1472.  
  1473. SizeChecker
  1474.  
  1475. StartupRoutine ${padd_size}
  1476.  
  1477. # Run PADD
  1478. NormalPADD
  1479. }
  1480.  
  1481. # Process all options (if present)
  1482. while [ "$#" -gt 0 ]; do
  1483. case "$1" in
  1484. "-j" | "--json" ) OutputJSON; exit 0;;
  1485. "-h" | "--help" ) DisplayHelp; exit 0;;
  1486. "-xoff" ) xOffset="$2"; xOffOrig="$2"; shift;;
  1487. "-yoff" ) yOffset="$2"; yOffOrig="$2"; shift;;
  1488. * ) DisplayHelp; exit 1;;
  1489. esac
  1490. shift
  1491. done
  1492.  
  1493. main
Add Comment
Please, Sign In to add comment