#!/bin/sh ################################################################################################################# # █████╗ ███████╗██╗ ██╗███████╗██╗ ██╗██████╗ ████████╗ ███╗ ███╗███████╗██████╗ ██╗ ██╗███╗ ██╗# #██╔══██╗██╔════╝██║ ██║██╔════╝██║ ██║██╔══██╗╚══██╔══╝ ████╗ ████║██╔════╝██╔══██╗██║ ██║████╗ ██║# #███████║███████╗██║ ██║███████╗██║ █╗ ██║██████╔╝ ██║█████╗██╔████╔██║█████╗ ██████╔╝██║ ██║██╔██╗ ██║# #██╔══██║╚════██║██║ ██║╚════██║██║███╗██║██╔══██╗ ██║╚════╝██║╚██╔╝██║██╔══╝ ██╔══██╗██║ ██║██║╚██╗██║# #██║ ██║███████║╚██████╔╝███████║╚███╔███╔╝██║ ██║ ██║ ██║ ╚═╝ ██║███████╗██║ ██║███████╗██║██║ ╚████║# #╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝╚══════╝╚═╝╚═╝ ╚═══╝# # █████╗ ██████╗ ██████╗ ██╗ ██╗ █████╗ ██████╗ ██████╗ ██╗ ██╗ ██████╗ ███╗ ███╗███████╗ # #██╔══██╗██╔══██╗██╔════╝ ██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║██╔═══██╗████╗ ████║██╔════╝ # #███████║██║ ██║██║ ███╗██║ ██║███████║██████╔╝██║ ██║███████║██║ ██║██╔████╔██║█████╗ # #██╔══██║██║ ██║██║ ██║██║ ██║██╔══██║██╔══██╗██║ ██║██╔══██║██║ ██║██║╚██╔╝██║██╔══╝ # #██║ ██║██████╔╝╚██████╔╝╚██████╔╝██║ ██║██║ ██║██████╔╝██║ ██║╚██████╔╝██║ ╚═╝ ██║███████╗ # #╚═╝ ╚═╝╚═════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ # #██╗███╗ ██╗███████╗████████╗ █████╗ ██╗ ██╗ ███████╗██████╗ Author: # #██║████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██║ ██║ ██╔════╝██╔══██╗ SomeWhereOverTheRainBow # #██║██╔██╗ ██║███████╗ ██║ ███████║██║ ██║ █████╗ ██████╔╝ Contributors: # #██║██║╚██╗██║╚════██║ ██║ ██╔══██║██║ ██║ ██╔══╝ ██╔══██╗ JackYaz,gspannu,thelonelycoder # #██║██║ ╚████║███████║ ██║ ██║ ██║███████╗███████╗███████╗██║ ██║ v1.7.4 # #╚═╝╚═╝ ╚═══╝╚══════╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝ # ################################################################################################################# # shellcheck disable=SC2016 # shellcheck disable=SC3043 # shellcheck disable=SC3045 # shellcheck disable=SC3057 export LC_ALL=C export PATH="/sbin:/bin:/usr/sbin:/usr/bin:${PATH}" AI_VERSION="v1.7.4" export AI_VERSION readonly LATEST_URL="https://api.github.com/repos/AdguardTeam/AdGuardHome/releases" varcnt=0 until [ -n "${REMOTE_VER}" ] && [ -n "${REMOTE_BETA}" ]; do [ -z "${REMOTE_VER}" ] && REMOTE_VER="$(curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -sL "${LATEST_URL}" | sed -n '/"prerelease": false,/q;p' | tail -4 | grep "tag_name" | cut -d \" -f 4 &)" || true [ -z "${REMOTE_BETA}" ] && REMOTE_BETA="$(curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -sL "${LATEST_URL}" | sed -n '/"prerelease": true,/q;p' | tail -4 | grep "tag_name" | cut -d \" -f 4 &)" || true varcnt="$((varcnt + 1))" if [ "${varcnt}" -le 84 ]; then wait else printf "%s/n" "One or more critical variables could not be set in a timely manner." "Please check your internet connection, or try again later." "The installer script will now exit." sleep 3s exit 1 fi done wait unset varcnt readonly REMOTE_VER REMOTE_BETA readonly ADDON_DIR="/jffs/addons/AdGuardHome.d" readonly BASE_DIR="/opt/etc" readonly TARG_DIR="${BASE_DIR}/AdGuardHome" readonly AGH_FILE="${TARG_DIR}/AdGuardHome" readonly CONF_FILE="${TARG_DIR}/.config" readonly YAML_FILE="${AGH_FILE}.yaml" readonly YAML_BAK="${YAML_FILE}.bak" readonly YAML_ERR="${YAML_FILE}.err" readonly YAML_ORI="${TARG_DIR}/.AdGuardHome.yaml.ori" SCRIPT_LOC="$(readlink -f "$0")" || true readonly SCRIPT_LOC BOLD="$(printf "\033[1m")" || true readonly BOLD NORM="$(printf "\033[0m")" || true readonly NORM INFO="$(printf "%s" "${BOLD} Info: ${NORM}")" || true readonly INFO ERROR="$(printf "%s" "${BOLD} *** Error: ${NORM}")" || true readonly ERROR WARNING="$(printf "%s" "${BOLD} * Warning: ${NORM}")" || true readonly WARNING INPUT="$(printf "%s" "${BOLD} => ${NORM}")" || true readonly INPUT _quote() { printf "%s\n" "$1" | sed 's/[]\/()$*.^|[]/\\&/g' } PTXT() { case "$1" in -n) shift while [ $# -gt 0 ]; do printf "%s" "$1" shift done ;; *) while [ $# -gt 0 ]; do printf "%s\n" "$1" shift done ;; esac } AdGuardHome_authen() { if [ -z "${PW1}" ] || [ -z "${PW2}" ]; then local USERNAME PTXT -n "${INPUT} Please enter AdGuardHome username${NORM}: " read -r USERNAME fi local PW1 PW2 PTXT -n "${INPUT} Please enter AdGuardHome password${NORM}: " read -rs PW1 PTXT " " PTXT -n "${INPUT} Please reenter AdGuardHome password${NORM}: " read -rs PW2 PTXT " " if [ -z "${PW1}" ] || [ -z "${PW2}" ] || [ "${PW1}" != "${PW2}" ]; then PTXT "${ERROR} Password entered incorrectly!" AdGuardHome_authen "$1" fi opkg install python3 python3-pip python3-bcrypt >/dev/null 2>&1 if opkg list-installed | grep -q apache; then opkg flag user apache apache-utils >/dev/null 2>&1 opkg remove apache apache-utils --force-removal-of-dependent-packages >/dev/null 2>&1 fi if ! opkg list-installed | grep -q python3-bcrypt; then case "$(/bin/uname -m)" in "aarch64" | "arm64") opkg install go >/dev/null 2>&1 ;; "armv7l" | *) if ! opkg list | grep -qw 'go_nohf'; then opkg install go >/dev/null 2>&1; else opkg install go_nohf >/dev/null 2>&1; fi ;; esac export PATH="${PATH}:/opt/bin/go/bin" export GOROOT="/opt/bin/go" export GOBIN="/opt/bin/" go install gophers.dev/cmds/bcrypt-tool@latest >/dev/null 2>&1 rm -rf go else pip3 install bcrypt >/dev/null 2>&1 fi local PW1_ENCRYPTED if opkg list-installed | grep -q python3-bcrypt; then PW1_ENCRYPTED="$(python -c 'import bcrypt; password = b"'"${PW1}"'"; print(bcrypt.hashpw(password, bcrypt.gensalt(prefix=b"2a", rounds=10)).decode("ascii"))')"; elif [ -f "/opt/bin/bcrypt-tool" ]; then PW1_ENCRYPTED="$(/opt/bin/bcrypt-tool hash "${PW1}" 10)"; else PTXT "${ERROR} Password could not be set!" "${ERROR} Please contact dev." end_op_message 1 return fi if [ "$1" -eq 0 ]; then yaml_nvars_delete "- name:" "${YAML_FILE}" yaml_nvars_delete " password:" "${YAML_FILE}" yaml_nvars_insert "users:" "\ password: ${PW1_ENCRYPTED}" "${YAML_FILE}" yaml_nvars_insert "users:" "- name: ${USERNAME}" "${YAML_FILE}" check_AdGuardHome_yaml "${YAML_FILE}" PTXT "${INFO} Starting AdGuardHome..." ( if [ -n "$(pidof AdGuardHome)" ]; then { if { service stop_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome stop; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome kill; }; then sleep 1s; else { killall -q -9 AdGuardHome 2>/dev/null; } rm -rf /opt/var/run/AdGuardHome.pid 2>/dev/null sleep 1s fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -lt "1" ]; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } ( if [ -z "$(pidof AdGuardHome)" ]; then { if { service start_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome start; }; then sleep 1s; else PTXT "${ERROR} Couldn't start AdGuardHome!" "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -eq "2" ]; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } if [ -z "$(pidof AdGuardHome)" ]; then PTXT "${ERROR} Couldn't start AdGuardHome!" \ "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi PTXT "${INFO} Please wait while we perform one last check." ( if [ -n "$(pidof AdGuardHome)" ]; then { if { service restart_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome restart; }; then sleep 1s; else PTXT "${ERROR} Couldn't start AdGuardHome!" "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until { check_connection && [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -eq "2" ]; }; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } PTXT "${INFO} AdGuardHome setup is complete." end_op_message 0 else PTXT "users:" \ "- name: ${USERNAME}" \ " password: ${PW1_ENCRYPTED}" >>"${YAML_ORI}" fi } backup_restore() { if [ "$1" = "BACKUP" ] && [ -d "${TARG_DIR}" ] && [ -f "${AGH_FILE}" ]; then if [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; then PTXT "${INFO} There is an old backup detected." local USE_OLD if read_yesno "Do you want to continue?(this will remove the old backup)"; then USE_OLD="NO"; else USE_OLD="YES"; fi if [ "${USE_OLD}" = "YES" ]; then PTXT "${INFO} Leaving Old Backup." end_op_message 1 elif [ "${USE_OLD}" = "NO" ]; then PTXT "${INFO} Removing Old Backup." rm -rf "${BASE_DIR}/backup_AdGuardHome.tar.gz" fi fi PTXT "${INFO} This operation will backup AdGuardHome(<4MB)to ENTWARE /opt/etc." \ "${INFO} Please wait a moment." tar -czvf "${BASE_DIR}/backup_AdGuardHome.tar.gz" -C "${TARG_DIR}" ../AdGuardHome/ >/dev/null 2>&1 PTXT "${INFO} Backup complete" [ -z "$2" ] && end_op_message 0 || return 0 elif [ "$1" = "BACKUP" ] && [ ! -d "${TARG_DIR}" ] && [ ! -f "${AGH_FILE}" ]; then PTXT "${ERROR} No ${AGH_FILE} to Backup!" end_op_message 1 fi if [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ] && [ "$1" = "RESTORE" ]; then PTXT "${INFO} Please wait a moment." tar -xzvf "${BASE_DIR}/backup_AdGuardHome.tar.gz" -C "${BASE_DIR}" >/dev/null 2>&1 chown "$(nvram get http_username)":root ${TARG_DIR}/* chmod 755 "${AGH_FILE}" chmod 644 "${YAML_FILE}" [ -f "/opt/sbin/AdGuardHome" ] && rm -rf /opt/sbin/AdGuardHome ln -sf "${AGH_FILE}" /opt/sbin/AdGuardHome inst_AdGuardHome "${1:-RESTORE}" elif [ ! -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ] && [ "$1" = "RESTORE" ]; then PTXT "${ERROR} No Backup found!" \ "${ERROR} Please make sure Backup Resides in ${BASE_DIR}" end_op_message 1 return fi } check_AdGuardHome_yaml() { [ ! -f "${YAML_FILE}" ] && return chmod 644 "${YAML_FILE}" PTXT "${INFO} Checking AdGuardHome configuration..." if ! "${AGH_FILE}" --check-config -c "${YAML_FILE}" --no-check-update -l "/dev/null"; then PTXT "${INFO} Moving invalid configuration file to ${YAML_ERR}." \ "${INFO} Operation will continue with clean config file." mv "${YAML_FILE}" "${YAML_ERR}" return 1 fi } ########################Modified Version of @Adamm Check_Connection#################################################### check_connection() { local livecheck="0" i while [ "${livecheck}" != "4" ]; do for i in google.com github.com snbforums.com; do if { ! nslookup "${i}" 127.0.0.1 >/dev/null 2>&1; } && { ping -q -w3 -c1 "${i}" >/dev/null 2>&1; }; then if { ! curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -Is "http://${i}" | head -n 1 >/dev/null 2>&1; } || { ! wget --no-cache --no-cookies --tries=3 --timeout=3 --waitretry=1 --retry-connrefused -q --spider "http://${i}" >/dev/null 2>&1; }; then sleep 1s continue fi fi return 0 done livecheck="$((livecheck + 1))" if [ "${livecheck}" != "4" ]; then sleep 10s continue fi return 1 done } ######################################################################################################################## check_dns_environment() { if [ -f "/opt/etc/init.d/S61stubby" ] || [ -f "/opt/sbin/stubby" ] || [ -f "/opt/bin/install_stubby" ] || [ -f "/jffs/scripts/install_stubby.sh" ] || [ -d "/jffs/dnscrypt" ] || [ -f "/opt/etc/init.d/S09dnscrypt-proxy" ]; then PTXT "${ERROR} Potential stubby or dnscrypt-proxy installation detected." \ "${ERROR} Please remove before attempting to continue." \ "${ERROR} Exiting..." exit 1 fi local NVCHECK NVCHECK="0" if [ "$(nvram get dnspriv_enable)" != "0" ]; then { nvram set dnspriv_enable="0"; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(pidof stubby)" ]; then { killall -q -9 stubby; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dhcp_dns1_x)" ] && [ "${NVCHECK}" != "0" ]; then { nvram set dhcp_dns1_x=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dhcp_dns2_x)" ] && [ "${NVCHECK}" != "0" ]; then { nvram set dhcp_dns2_x=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dhcpd_dns_router)" != "1" ] && [ "${NVCHECK}" != "0" ]; then { nvram set dhcpd_dns_router="1"; } NVCHECK="$((NVCHECK + 1))" fi if [ "${NVCHECK}" != "0" ]; then { nvram commit; } { service restart_dnsmasq >/dev/null 2>&1; } (while { ! check_connection; }; do sleep 1s; done) & local PID="$!" wait "${PID}" 2>/dev/null fi PTXT "${INFO} DNS Environment is Ready." } check_dns_filter() { local NVCHECK USE_SOME NVCHECK="0" if [ "$1" -eq 0 ]; then if [ "$(nvram get dnsfilter_enable_x)" -ne 0 ]; then { nvram set dnsfilter_enable_x="0"; } NVCHECK="$((NVCHECK + 1))" fi PTXT "${INFO} DNS will not be forced through to AdGuardHome." fi if [ "$1" -eq 1 ]; then if [ "$(nvram get dnsfilter_enable_x)" -ne 1 ]; then { nvram set dnsfilter_enable_x="1"; } NVCHECK="$((NVCHECK + 1))" fi PTXT "${INFO} You can choose to keep any custom dnsfilter values by only redirect non-custom traffic or send all traffic through to AdGuardHome." if read_yesno "Do you want to redirect only NON-CUSTOM DNS resolutions on your network through to AdGuardHome?"; then USE_SOME="0"; else USE_SOME="1"; fi if [ "$USE_SOME" -eq 0 ]; then if [ "$(nvram get dnsfilter_mode)" != "11" ]; then { nvram set dnsfilter_mode="11"; } NVCHECK="$((NVCHECK + 1))" fi PTXT "${INFO} DNSFilter is set to control DNS through to AdGuardHome, while leaving any Custom Rules and Values." fi if [ "$USE_SOME" -eq 1 ]; then if [ "$(nvram get dnsfilter_custom1)" ]; then { nvram set dnsfilter_custom1=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_custom2)" ]; then { nvram set dnsfilter_custom2=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_custom3)" ]; then { nvram set dnsfilter_custom3=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_mode)" != "11" ]; then { nvram set dnsfilter_mode="11"; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_rulelist)" ]; then { nvram set dnsfilter_rulelist=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_rulelist1)" ]; then { nvram set dnsfilter_rulelist1=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_rulelist2)" ]; then { nvram set dnsfilter_rulelist2=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_rulelist3)" ]; then { nvram set dnsfilter_rulelist3=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_rulelist4)" ]; then { nvram set dnsfilter_rulelist4=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dnsfilter_rulelist5)" ]; then { nvram set dnsfilter_rulelist5=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dhcp_dns1_x)" ]; then { nvram set dhcp_dns1_x=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dhcp_dns2_x)" ]; then { nvram set dhcp_dns2_x=""; } NVCHECK="$((NVCHECK + 1))" fi if [ "$(nvram get dhcpd_dns_router)" != "1" ]; then { nvram set dhcpd_dns_router="1"; } NVCHECK="$((NVCHECK + 1))" fi PTXT "${INFO} DNS is set to redirect All DNS resolutions through to AdGuardHome." fi fi if [ "${NVCHECK}" != "0" ]; then { nvram commit; } { service "restart_firewall;restart_dnsmasq" >/dev/null 2>&1; } (while { ! check_connection; }; do sleep 1s; done) & local PID="$!" wait "${PID}" 2>/dev/null fi } check_dns_local() { local LOCAL_CACHE case "$1" in 0) LOCAL_CACHE="NO" write_conf ADGUARD_LOCAL "\"${LOCAL_CACHE}\"" ;; 1) LOCAL_CACHE="YES" write_conf ADGUARD_LOCAL "\"${LOCAL_CACHE}\"" ;; esac } check_jffs_enabled() { if [ "$(nvram get jffs2_format)" = "1" ]; then PTXT "${ERROR} JFFS partition is scheduled to be reformatted." \ "${ERROR} Please reboot to format or disable that setting and try again." \ "${ERROR} Exiting..." exit 1 fi local JFFS2_SCRIPTS JFFS2_ENABLED jffs2_on JFFS2_SCRIPTS="$(nvram get jffs2_scripts)" [ -z "$(nvram get jffs2_enable)" ] && JFFS2_ENABLED="$(nvram get jffs2_on)" || JFFS2_ENABLED="$(nvram get jffs2_enable)" [ -z "$(nvram get jffs2_enable)" ] && jffs2_on="jffs2_on" || jffs2_on="jffs2_enable" if [ "${JFFS2_ENABLED}" -ne 1 ] || [ "${JFFS2_SCRIPTS}" -ne 1 ]; then PTXT "${INFO} JFFS custom scripts and configs are not enabled." \ "${INFO} Enabling them now!" nvram set ${jffs2_on}="1" nvram set jffs2_scripts="1" nvram commit else PTXT "${INFO} JFFS custom scripts and configs are already enabled." fi } check_version() { local RMNSTALL LINSTALL MD5SUM_L MD5SUM_R if [ -f "${TARG_DIR}/installer" ] && [ -f "${AGH_FILE}" ] && [ -z "$2" ]; then if [ -n "$1" ] || { check_connection; }; then local varcnt=0 until [ -n "${LINSTALL}" ] && [ -n "${RMNSTALL}" ] && [ -n "${MD5SUM_L}" ] && [ -n "${MD5SUM_R}" ]; do [ -z "${LINSTALL}" ] && LINSTALL="$(awk '{ print }' "${TARG_DIR}/installer" | grep -m1 "^AI_VERSION=" | grep -oE '[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})' &)" || true [ -z "${RMNSTALL}" ] && RMNSTALL="$(curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -sL "${RURL}/installer" | grep -m1 "^AI_VERSION=" | grep -oE '[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})' &)" || true [ -z "${MD5SUM_L}" ] && MD5SUM_L="$(md5sum "${TARG_DIR}/installer" | cut -d' ' -f1 &)" || true [ -z "${MD5SUM_R}" ] && MD5SUM_R="$(curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -fsL "${RURL}/installer" | md5sum | awk '{print $1}' &)" || true varcnt="$((varcnt + 1))" if [ "${varcnt}" -le 84 ]; then wait else printf "%s/n" "One or more critical variables could not be set in a timely manner." "Please check your internet connection, or try again later." "The installer script will now exit." sleep 3s exit 1 fi done wait if { [ -n "${LINSTALL}" ] && [ -n "${RMNSTALL}" ]; }; then [ -z "${LINSTALL}" ] && exit 1 [ -z "${RMNSTALL}" ] && exit 1 if [ "${RMNSTALL}" != "${LINSTALL}" ]; then PTXT "${INFO} New AI_VERSION=v${RMNSTALL} Available!" \ "${INFO} Run Option 1 of the Installer to upgrade Asuswrt-Merlin-AdGuardHome-Installer." AUTO_UPDATE="update" elif [ "${MD5SUM_R}" = "${MD5SUM_L}" ]; then PTXT "${INFO} AI_VERSION=v${LINSTALL}" else PTXT "${INFO} AI_VERSION=v${LINSTALL}, but a New Minor Update is Available!" \ "${INFO} Run Option 1 of the Installer to upgrade Asuswrt-Merlin-AdGuardHome-Installer." AUTO_UPDATE="update" fi local ADGUARD_BRANCH ADGUARD_BRANCH="$(awk -F'=' '/ADGUARD_BRANCH/ {print $2}' "$CONF_FILE" | sed -e 's/^"//' -e 's/"$//')" [ -z "$(${AGH_FILE} --version | cut -d" " -f4-)" ] && exit 1 [ -z "${REMOTE_VER}" ] && exit 1 [ -z "${REMOTE_BETA}" ] && exit 1 if { [ "${REMOTE_VER}" != "$(${AGH_FILE} --version | cut -d" " -f4-)" ] && [ "${ADGUARD_BRANCH}" = "release" ]; } || { [ "${REMOTE_BETA}" != "$(${AGH_FILE} --version | cut -d" " -f4-)" ] && [ "${ADGUARD_BRANCH}" = "beta" ]; }; then case "${ADGUARD_BRANCH}" in release) PTXT "${INFO} New RELEASE ADGUARDHOME_VER=${REMOTE_VER} Available!" ;; beta) PTXT "${INFO} New BETA ADGUARDHOME_VER=${REMOTE_BETA} Available!" ;; esac AUTO_UPDATE="update" else case "${ADGUARD_BRANCH}" in release) PTXT "${INFO} ADGUARDHOME_BUILD=Release" ;; beta) PTXT "${INFO} ADGUARDHOME_BUILD=Beta" ;; edge) PTXT "${INFO} ADGUARDHOME_BUILD=Edge" ;; esac fi PTXT "${INFO} ADGUARDHOME_VER=$(${AGH_FILE} --version | cut -d" " -f4-)" \ "${INFO} Run Option 1 to if you want to change builds or upgrade when updates are available." \ "${INFO} To visit AdGuardHome," \ "${INFO} please go to http://$(nvram get lan_ipaddr):$(awk -F: '/.*address\:.*/{ print $3 }' ${YAML_FILE})." \ "${INFO} You can use the WebUI to change things to your liking!" \ "${INFO} Stop by https://github.com/AdguardTeam/AdGuardHome/wiki " \ "${INFO} for any configuration needs. " fi elif [ -z "${LINSTALL}" ] && [ -z "${RMNSTALL}" ] && [ -z "$1" ]; then while { ! check_connection; }; do sleep 1s; done && check_version x else check_version x x fi fi } choose_branch() { if [ "$1" = "1" ]; then del_conf ADGUARD_BRANCH fi local BUILD if [ ! -f "${CONF_FILE}" ] || [ -z "$(awk -F'=' '/ADGUARD_BRANCH/ {print $2}' "${CONF_FILE}" | sed -e 's/^"//' -e 's/"$//')" ]; then PTXT "${INFO} Choose which build of AdGuardHome to install:" \ " 1) Release" \ " 2) Beta" \ " 3) Edge" read_input_num "Select your mode" 1 3 case "${CHOSEN}" in 1) BUILD=release write_conf ADGUARD_BRANCH "\"${BUILD}\"" ;; 2) BUILD=beta write_conf ADGUARD_BRANCH "\"${BUILD}\"" ;; 3) BUILD=edge write_conf ADGUARD_BRANCH "\"${BUILD}\"" ;; esac else if read_yesno "Do you want to switch AdGuardHome builds?"; then choose_branch 1; else PTXT "${INFO} continuing without changing builds."; fi fi } cleanup() { mv "/opt/etc/init.d/S61AdGuardHome" "/opt/etc/init.d/S99AdGuardHome" >/dev/null 2>&1 [ "$(cru l | grep -c "S99AdGuardHome")" -gt 0 ] && cru -d "S99AdGuardHome" if [ -f "${TARG_DIR}/localtime" ]; then mv "${TARG_DIR}/localtime" "${ADDON_DIR}/localtime"; fi if opkg list-installed | grep -q apache; then opkg flag user apache apache-utils >/dev/null 2>&1 opkg remove apache apache-utils --force-removal-of-dependent-packages >/dev/null 2>&1 fi } create_dir() { if ! mkdir -p "${1}"; then PTXT "${ERROR} Unable to create ${1}!" return 1 fi } del_between_magic() { local TARG MAGIC BOUNDS TARG="$1" MAGIC="$2" [ -f "${TARG}" ] || return BOUNDS="$(awk -v PATT="${MAGIC}" '($0 ~ PATT) {printf NR","}' "${TARG}")" if [ "${BOUNDS}" ]; then sed -i "${BOUNDS%,}d" "${TARG}" fi } del_conf() { [ ! -f "${CONF_FILE}" ] && return local KEY for KEY in "$@"; do sed -i "/^${KEY}=.*$/d" "${CONF_FILE}" done } del_jffs_script() { local TARG LINE_NUM LINE_ABOVE OP TARG="$1" [ -f "${TARG}" ] || return if [ "$2" ]; then OP="${2:0:1}" if [ "$OP" = "!" ]; then LINE_NUM="$(grep -n -F "[ -x ${ADDON_DIR}/" "${TARG}" | grep -v "$(_quote "$2")" | cut -d':' -f1)" else LINE_NUM="$(grep -n -F "[ -x ${ADDON_DIR}/" "${TARG}" | grep "$(_quote "$2")" | cut -d':' -f1)" fi else LINE_NUM="$(grep -n -F "[ -x ${ADDON_DIR}/" "${TARG}" | cut -d':' -f1)" fi [ -z "${LINE_NUM}" ] && return sed -i "${LINE_NUM}d" "${TARG}" if [ "${LINE_NUM}" -gt 1 ]; then LINE_NUM="$((LINE_NUM - 1))" LINE_ABOVE="$(sed "${LINE_NUM}q;d" "${TARG}")" [ -z "${LINE_ABOVE}" ] && sed -i "${LINE_NUM}d" "${TARG}" fi [ "$(awk '{ print }' "${TARG}")" = "#!/bin/sh" ] && rm -f "${TARG}" } download_file() { local TARG PERM URL RET FILENAME MD5SUM_OLD MD5SUM_CURR TARG="$1" shift PERM="$1" shift for URL in "$@"; do FILENAME="$(basename "${URL}")" local varcnt="0" until [ -n "${MD5SUM_CURR}" ]; do if [ -f "${TARG}/${FILENAME}" ]; then [ -z "${MD5SUM_OLD}" ] && MD5SUM_OLD="$(md5sum "${TARG}/${FILENAME}" | cut -d' ' -f1 &)" || true fi [ -z "${MD5SUM_CURR}" ] && MD5SUM_CURR="$(curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -fsL "${URL}" | md5sum | awk '{print $1}' &)" || true varcnt="$((varcnt + 1))" if [ "${varcnt}" -eq 1 ]; then wait else break fi done wait if [ "$(PTXT -n "${MD5SUM_CURR}" | wc -c)" -eq 32 ] && [ "${MD5SUM_CURR}" = "${MD5SUM_OLD}" ]; then PTXT "${INFO} ${FILENAME} is up to date. Skipping..." else local COUNT COUNT="0" while [ "${COUNT}" -lt 3 ]; do PTXT "${INFO} Downloading ${FILENAME}" if curl --retry 3 --connect-timeout 3 --retry-delay 1 --max-time $((3 * 5)) --retry-connrefused -L -k -s "${URL}" -o "${TARG}/${FILENAME}"; then chmod "${PERM}" "${TARG}/${FILENAME}" break fi COUNT="$((COUNT + 1))" done if [ "${COUNT}" -eq 3 ]; then PTXT "${ERROR} Unable to download ${BOLD}${URL}${NORM}" if [ -z "${RET}" ]; then RET="1"; else RET="$((RET + 1))"; fi fi fi done if [ -z "${RET}" ]; then RET="0"; else PTXT "${ERROR} One or more download failures has occured." "${ERROR} It is recommended to rerun the installer, or restore from a backup!"; fi return "${RET}" } end_op_message() { case "${1:-0}" in 0) PTXT "${INFO} Operation completed, returning to Main Menu. You can quit or continue." ;; 1) PTXT "${INFO} Operation aborted, returning to Main Menu. You can quit or continue." ;; 2) PTXT "${INFO} Abnormal operations, returning to Main Menu. You can quit or continue." ;; esac PTXT "=====================================================" PTXT " " PTXT " " sleep 3s && clear if [ -f "${TARG_DIR}/installer" ]; then chmod 755 "${TARG_DIR}/installer" >/dev/null 2>&1 exec "${TARG_DIR}/installer" "${BRANCH}" && exit elif [ ! -f "${TARG_DIR}/installer" ] && [ -f "${SCRIPT_LOC}" ]; then chmod 755 "${SCRIPT_LOC}" >/dev/null 2>&1 exec "${SCRIPT_LOC}" "${BRANCH}" && exit elif [ -f "${HOME}/installer" ]; then chmod 755 "${HOME}/installer" exec "${HOME}/installer" "${BRANCH}" && exit else clear && { end_op_header 2>/dev/null; } && exit fi } end_op_header() { sed -n -e "1,$(($(grep -wn 'esac' "$0" | cut -d':' -f1 | tail -n1) + 1))p" "$0" >"${0}.tmp" chmod 755 "${0}.tmp" exec "${0}.tmp" "${BRANCH}" && rm -rf "${0}.tmp" } inst_AdGuardHome() { local ADGUARD_TAR ADGUARD_TAR="AdGuardHome_${ADGUARD_ARCH}.tar.gz" if [ -z "$2" ]; then if [ "${1:-RESTORE}" != "RESTORE" ]; then if [ ! -d "${TARG_DIR}" ] && [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; then PTXT "${INFO} Backup is detected." local USE_OLD if read_yesno "Do you want Restore instead?"; then USE_OLD="YES"; else USE_OLD="NO"; fi if [ "${USE_OLD}" = "YES" ]; then PTXT "${INFO} Installing from an old backup!" backup_restore RESTORE elif [ "${USE_OLD}" = "NO" ]; then PTXT "${INFO} Continuing without restoring from backup!" fi elif [ -d "${TARG_DIR}" ] && [ -f "${AGH_FILE}" ] && [ ! -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; then if read_yesno "Do you want create a backup before updating?"; then backup_restore BACKUP 0; else PTXT "${INFO} continuing without making a backup."; fi fi fi if [ -z "${REMOTE_VER}" ] || { ! check_connection; }; then PTXT "${ERROR} Unable to detect the Internet!" end_op_message 1 return fi if ! create_dir "${TARG_DIR}"; then end_op_message 1 return fi if ! download_file "${TARG_DIR}" 755 "${RURL}/installer" || ! awk '{ print }' "${TARG_DIR}/installer" | grep -m1 "^AI_VERSION=" | grep -qoE '[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})'; then PTXT "${ERROR} Failed to download installer." end_op_message 1 return fi if [ "${1:-RESTORE}" != "RESTORE" ]; then if [ "${1:-install}" != "update" ]; then choose_branch; fi local ADGUARD_BRANCH ADGUARD_BRANCH="$(awk -F'=' '/ADGUARD_BRANCH/ {print $2}' "${CONF_FILE}" | sed -e 's/^"//' -e 's/"$//')" if [ ! -f "${AGH_FILE}" ]; then inst_AdGuardHome "${1:-install}" "$ADGUARD_BRANCH" else [ -z "$("${AGH_FILE}" --version | cut -d" " -f4-)" ] && exit 1 [ -z "${REMOTE_VER}" ] && exit 1 [ -z "${REMOTE_BETA}" ] && exit 1 if { [ "${REMOTE_VER}" != "$("${AGH_FILE}" --version | cut -d" " -f4-)" ] && [ "${ADGUARD_BRANCH}" = "release" ]; } || { [ "${REMOTE_BETA}" != "$("${AGH_FILE}" --version | cut -d" " -f4-)" ] && [ "${ADGUARD_BRANCH}" = "beta" ]; } || [ "${ADGUARD_BRANCH}" = "edge" ]; then case "${ADGUARD_BRANCH}" in release) PTXT "${INFO} New RELEASE ADGUARDHOME_VER=${REMOTE_VER} Available!" \ "${INFO} Updating ADGUARDHOME_VER=$(${AGH_FILE} --version | cut -d" " -f4-) to ${REMOTE_VER}." ;; beta) PTXT "${INFO} New BETA ADGUARDHOME_VER=${REMOTE_BETA} Available!" \ "${INFO} Updating ADGUARDHOME_VER=$(${AGH_FILE} --version | cut -d" " -f4-) to ${REMOTE_BETA}." ;; edge) PTXT "${INFO} ADGUARDHOME_BUILD=Edge" \ "${INFO} Downloading the lastest Edge version to replace ADGUARDHOME_VER=$(${AGH_FILE} --version | cut -d" " -f4-)." ;; esac inst_AdGuardHome "${1:-update}" "${ADGUARD_BRANCH}" else case "${ADGUARD_BRANCH}" in release) PTXT "${INFO} ADGUARDHOME_BUILD=Release" \ "${INFO} No new release version available." \ "${INFO} ADGUARDHOME_VER=$(${AGH_FILE} --version | cut -d" " -f4-)" ;; beta) PTXT "${INFO} ADGUARDHOME_BUILD=Beta" \ "${INFO} No new beta version available." \ "${INFO} ADGUARDHOME_VER=$(${AGH_FILE} --version | cut -d" " -f4-)" ;; esac fi fi fi else if ! download_file "${BASE_DIR}" 644 "https://static.adguard.com/adguardhome/${2}/${ADGUARD_TAR}"; then PTXT "${ERROR} Unable to download AdGuardHome package for your router" end_op_message 1 return fi tar xzv -C "${BASE_DIR}" -f "${BASE_DIR}/${ADGUARD_TAR}" >/dev/null 2>&1 chown "$(nvram get http_username)":root ${TARG_DIR}/* rm -r "${BASE_DIR:?}/${ADGUARD_TAR}" chmod 755 "${AGH_FILE}" [ -f "/opt/sbin/AdGuardHome" ] && rm -rf /opt/sbin/AdGuardHome if ! ln -sf "${AGH_FILE}" /opt/sbin/AdGuardHome || [ -z "$("${AGH_FILE}" --version | cut -d" " -f4-)" ]; then PTXT "${ERROR} Failed to download AdGuardHome package for your router" end_op_message 1 return fi fi create_dir "${ADDON_DIR}" download_file "${ADDON_DIR}" 755 "${RURL}/AdGuardHome.sh" download_file "/opt/etc/init.d" 755 "${RURL}/S99AdGuardHome" download_file "/opt/etc/init.d" 644 "${RURL}/rc.func.AdGuardHome" [ -f "/jffs/scripts/dnsmasq.postconf" ] && yaml_nvars_delete "#Asuswrt-Merlin AdGuardHome Installer" /jffs/scripts/dnsmasq.postconf for i in init-start services-stop; do { if { ! grep -q "${ADDON_DIR}/AdGuardHome.sh $i &" "/jffs/scripts/${i}" && grep -q "${ADDON_DIR}/AdGuardHome.sh $i" "/jffs/scripts/${i}"; }; then del_jffs_script "/jffs/scripts/${i}"; fi; }; done write_manager_script /jffs/scripts/init-start "init-start &" write_manager_script /jffs/scripts/services-stop "services-stop &" write_manager_script /jffs/scripts/dnsmasq.postconf dnsmasq del_between_magic /jffs/scripts/service-event-end '# Asuswrt-Merlin-AdGuardHome-Installer' write_command_script /jffs/scripts/service-event-end 'if printf "%s" "$@" | /bin/grep -qE "^(((start|stop|restart|kill|reload)_?.*AdGuardHome)$)"; then { sh /jffs/addons/AdGuardHome.d/AdGuardHome.sh "$(printf "%s" "$@" | /bin/grep -oE "(start|stop|restart|kill|reload)")" x & }; fi # Asuswrt-Merlin-AdGuardHome-Installer' if [ ! -f "${ADDON_DIR}/localtime" ]; then PTXT "${INFO} Before setting up AdGuardHome, please choose your right timezone!" set_timezone fi if ! setup_AdGuardHome "" "${1:-install}"; then end_op_message 1 return fi PTXT "${INFO} Starting AdGuardHome..." ( if [ -n "$(pidof AdGuardHome)" ]; then { if { service stop_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome stop; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome kill; }; then sleep 1s; else { killall -q -9 AdGuardHome 2>/dev/null; } rm -rf /opt/var/run/AdGuardHome.pid 2>/dev/null sleep 1s fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -lt "1" ]; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } ( if [ -z "$(pidof AdGuardHome)" ]; then { if { service start_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome start; }; then sleep 1s; else PTXT "${ERROR} Couldn't start AdGuardHome!" "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -eq "2" ]; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } if [ -z "$(pidof AdGuardHome)" ]; then PTXT "${ERROR} Couldn't start AdGuardHome!" \ "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi PTXT "${INFO} Please wait while we perform one last check." ( if [ -n "$(pidof AdGuardHome)" ]; then { if { service restart_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome restart; }; then sleep 1s; else PTXT "${ERROR} Couldn't start AdGuardHome!" "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until { check_connection && [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -eq "2" ]; }; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } PTXT "${INFO} AdGuardHome setup is complete." end_op_message 0 } read_input_dns() { PTXT -n "${INPUT} ${1} ${BOLD}${2}: ${NORM}" local DNS_SERVER read -r DNS_SERVER [ -z "${DNS_SERVER}" ] && DNS_SERVER="$2" if ! PTXT "${DNS_SERVER}" | grep -qoE "\b((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b"; then PTXT "${ERROR} Invalid DNS server address entered" read_input_dns "$@" fi if [ "${DNS_SERVER1}" = "${DNS_SERVER}" ]; then PTXT "${ERROR} ${DNS_SERVER} DNS server address already entered, please try again!" read_input_dns "$@" fi case "$1" in "Default is") BOOTSTRAP1="${DNS_SERVER}" DNS_SERVER1="${DNS_SERVER}" ;; "2nd Default is") BOOTSTRAP2="${DNS_SERVER}" ;; esac } read_input_num() { local RANGE [ -z "$4" ] && [ -z "$5" ] && [ -z "$6" ] && RANGE="[${2}-${3}]" [ -n "$4" ] && [ -z "$5" ] && [ -z "$6" ] && RANGE="[${2}-${3}/${4}]" [ -n "$4" ] && [ -n "$5" ] && [ -z "$6" ] && RANGE="[${2}-${3}/${4}/${5}]" [ -n "$4" ] && [ -n "$5" ] && [ -n "$6" ] && RANGE="[${2}-${3}/${4}/${5}/${6}]" PTXT -n "${INPUT} ${1}, ${BOLD}${RANGE}${NORM}: " read -r CHOSEN case "$1" in *) if [ -z "${CHOSEN}" ]; then PTXT "${ERROR} Invalid character(s) entered! Retrying..." read_input_num "$@" return fi ;; esac case "${CHOSEN}" in "$4" | "$5" | "$6") return 1 ;; "$2" | "$3" | *) if ! PTXT "${CHOSEN}" | grep -qE '^[0-9]+$'; then PTXT "${ERROR} Invalid character(s) entered! Retrying..." read_input_num "$@" return fi if [ "${CHOSEN}" -lt "$2" ] || [ "${CHOSEN}" -gt "$3" ]; then PTXT "${ERROR} Chosen number is not in range! Retrying..." read_input_num "$@" return fi ;; esac } read_input_port() { PTXT -n "${INPUT} ${1} ${BOLD}${2}: ${NORM}" read -r WEB_PORT [ -z "${WEB_PORT}" ] && WEB_PORT="$2" if ! PTXT "${WEB_PORT}" | grep -qoE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$' || { [ "${WEB_PORT}" -lt 3000 ] || [ "${WEB_PORT}" -gt 65535 ]; }; then PTXT "${ERROR} Invalid Web Port entered, only Web Ports residing within the port range 3000-65535 are selectable." read_input_port "$@" fi if netstat -nlp 2>/dev/null | grep -qE "(:${WEB_PORT}[[:space:]])"; then PTXT "${ERROR} ${WEB_PORT} already in use, please choose a different port!" read_input_port "$@" fi } read_yesno() { PTXT -n "${INPUT} ${1} ${BOLD}[y/n]${NORM}: " local YESNO read -r YESNO case "${YESNO}" in y | Y) return 0 ;; n | N) return 1 ;; *) PTXT "${ERROR} Invalid input!" read_yesno "$@" ;; esac } set_timezone() { local TMP TZ_ARCH TZ_DATA INDEX TZ_FILE TMP="/root" TZ_ARCH="$(uname -m)" case "${TZ_ARCH}" in "aarch64" | "arm64") TZ_ARCH="aarch64" ;; "armv7l") TZ_ARCH="arm" ;; esac TZ_DATA="tzdata-2021e-1-${TZ_ARCH}.pkg.tar.bz2" opkg install column >/dev/null 2>&1 download_file "${TMP}" 644 "${RURL}/${TZ_DATA}" INDEX="$(tar tjf "${TMP}/${TZ_DATA}" | awk -F'/' '!/\/$/ && /\/posix\//' | wc -l)" PTXT "${INFO} Available timezones/locations:" tar tjf "${TMP}/${TZ_DATA}" | awk -F'/' '!/\/$/ && /\/posix\//' | sort | cut -d'/' -f2- | awk -v INDEX=0 -F'/' '!/\/$/ {++INDEX;printf " " INDEX") ";for (i=5; i/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome restart; }; then sleep 1s; else PTXT "${ERROR} Couldn't start AdGuardHome!" "${ERROR} Please send WebUI System Log to dev." end_op_message 1 return fi; }; fi sleep 1s ) & local PID="$!" wait "${PID}" 2>/dev/null ({ until { check_connection && [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -eq "2" ]; }; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } PTXT "${INFO} AdGuardHome setup is complete." end_op_message 0 else end_op_message 0 fi fi return "${RET}" } setup_AdGuardHome_impl() { local SCHEMA_VER="$("${AGH_FILE}" -v --version | awk '/Schema[[:space:]]version:/{ print $NF }' 2>/dev/null)" if [ -z "${SCHEMA_VER}" ]; then SCHEMA_VER="27"; fi if [ -z "$1" ] && [ -f "${YAML_FILE}" ]; then ############################################################ Drop-in code to be removed/modified as needed. ############################################################################################################################################################# if ! awk -F'=' '/ADGUARD_WEBUI_PORT/ {print $2}' "${CONF_FILE}" 2>/dev/null | sed -e 's/^"//' -e 's/"$//' | grep -qoE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$'; then PTXT "${INFO} The Web Port for AdGuardHome WebUI has not been manually configured." \ "${INFO} Please set the preferred Web Port for AdGuardHome WebUI." \ "${INFO} The chosen port must reside within the port range 3000-65535" \ "${INFO} and not already in use by any other service." read_input_port "Default is" 3000 write_conf ADGUARD_WEBUI_PORT "\"${WEB_PORT}\"" yaml_nvars_replace " address: 0.0.0.0:$(awk -F':' '/.*address\:.*/{ print $3 }' ${YAML_FILE} | grep -oE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$')" " address: 0.0.0.0:${WEB_PORT}" "${YAML_FILE}" elif awk -F'=' '/ADGUARD_WEBUI_PORT/ {print $2}' "${CONF_FILE}" 2>/dev/null | sed -e 's/^"//' -e 's/"$//' | grep -qoE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$' && [ "$(awk -F'=' '/ADGUARD_WEBUI_PORT/ {print $2}' "${CONF_FILE}" | sed -e 's/^"//' -e 's/"$//' | grep -oE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$')" != "$(awk -F':' '/.*address\:.*/{ print $3 }' "${YAML_FILE}" | grep -oE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$')" ]; then yaml_nvars_replace " address: 0.0.0.0:$(awk -F':' '/.*address\:.*/{ print $3 }' ${YAML_FILE} | grep -oE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$')" " address: 0.0.0.0:$(awk -F'=' '/ADGUARD_WEBUI_PORT/ {print $2}' ${CONF_FILE} | sed -e 's/^"//' -e 's/"$//' | grep -oE '^(3000|[3-9][0-9]{3}|[1-5][0-9]{4}|6[0-4][0-9]{3}|65[0-4][0-9]{2}|655[0-2][0-9]|6553[0-5])$')" "${YAML_FILE}" fi ########################################################################################################################################################################################################################################################################## if [ -n "$(awk -F': ' '/.*schema\:.*/{ print $2 }' "${YAML_FILE}")" ] && [ "$(awk -F': ' '/.*schema\:.*/{ print $2 }' "${YAML_FILE}")" != "${SCHEMA_VER}" ]; then yaml_nvars_replace "schema_version: $(awk -F': ' '/.*schema\:.*/{ print $2 }' ${YAML_FILE})" "schema_version: ${SCHEMA_VER}" "${YAML_FILE}"; fi if ! check_AdGuardHome_yaml; then setup_AdGuardHome_impl x return fi PTXT "${INFO} Found previous AdGuardHome config file" if read_yesno "Do you want to use this file without reconfiguring?"; then PTXT "${INFO} Use previous settings file"; else setup_AdGuardHome_impl x; fi else if [ -f "${YAML_FILE}" ]; then if [ "$1" = "reconfig" ]; then if ! check_AdGuardHome_yaml; then setup_AdGuardHome_impl x return fi PTXT "${INFO} Found previous AdGuardHome config file" fi PTXT "${INFO} How do you want to reconfigure:" \ "${INFO} 1) Use previous settings file" \ "${INFO} 2) Restore original configuration selections" \ "${INFO} 3) Choose new configuration selections" read_input_num "Your selection" 1 3 case "${CHOSEN}" in 1) PTXT "${INFO} Using previous settings file" ;; 2 | 3) PTXT "${INFO} Backing up previous settings file..." mv "${YAML_FILE}" "${YAML_BAK}" if [ "${CHOSEN}" = "3" ]; then rm -rf "${YAML_ORI}" 2>/dev/null else cp -f "${YAML_ORI}" "${YAML_FILE}" fi ;; esac elif [ ! -f "${YAML_FILE}" ] && [ -f "${YAML_ORI}" ]; then cp -f "${YAML_ORI}" "${YAML_FILE}" fi case "${2:-reconfig}" in "install" | "reconfig") if [ ! -f "${YAML_FILE}" ] || [ "$2" = "reconfig" ]; then if read_yesno "Do you want to redirect all DNS resolutions on your network through to AdGuardHome?"; then check_dns_filter 1; else check_dns_filter 0; fi if [ "$(nvram get dns_local_cache)" != "1" ] && read_yesno "Do you want to run AdGuardHome as a local caching DNS service which includes router traffic?"; then check_dns_local 1; else check_dns_local 0; fi fi if [ ! -f "${YAML_ORI}" ] && [ ! -f "${YAML_FILE}" ]; then PTXT "${INFO} Requesting entries for AdGuardHome initial configuration..." \ "${INFO} Please set the preferred Web Port for AdGuardHome WebUI." \ "${INFO} The chosen port must reside within the port range 3000-65535" \ "${INFO} and not already in use by any other service." read_input_port "Default is" 3000 write_conf ADGUARD_WEBUI_PORT "\"${WEB_PORT}\"" PTXT "http:" \ " address: 0.0.0.0:${WEB_PORT}" >"${YAML_ORI}" PTXT "${INFO} Set the Username and Password which will be encrypted to the yaml file." AdGuardHome_authen 1 PTXT "dns:" \ " bind_hosts:" \ " - 0.0.0.0" \ " port: 53" >>"${YAML_ORI}" PTXT "${INFO} Set the DNS server(s) for initializing AdGuardHome" \ "${INFO} and router services (e.g. ntp) at boot" read_input_dns "Default is" 9.9.9.9 read_input_dns "2nd Default is" 8.8.8.8 local DOMAIN NET_ADDR NET_ADDR6 LAN_IF LAN_IF="$(nvram get lan_ifname)" [ -n "${LAN_IF}" ] && NET_ADDR="$(ip -o -4 addr list "${LAN_IF}" | awk 'NR==1{ split($4, ip_addr, "/"); print ip_addr[1] }')" || NET_ADDR="$(nvram get lan_ipaddr)" [ -n "${LAN_IF}" ] && NET_ADDR6="$(ip -o -6 addr list "${LAN_IF}" scope global | awk 'NR==1{ split($4, ip_addr, "/"); print ip_addr[1] }')" || NET_ADDR6="$(nvram get ipv6_rtr_addr)" [ -n "$(nvram get lan_domain)" ] && DOMAIN="$(nvram get lan_domain)" && write_conf ADGUARD_DOMAIN "\"no\"" [ -z "$(nvram get lan_domain)" ] && DOMAIN="lan" && nvram set lan_domain="${DOMAIN}" && nvram commit && write_conf ADGUARD_DOMAIN "\"yes\"" PTXT " upstream_dns:" >>"${YAML_ORI}" [ -n "${NET_ADDR6}" ] && PTXT " - '[/$(printf "%s\n" "${NET_ADDR6}" | sed 's/.$//' | awk -F: '{for(i=1;i<=NF;i++)x=x""sprintf (":%4s", $i);gsub(/ /,"0",x); printf x}' | cut -c 2- | cut -c 1-20 | sed 's/://g;s/^.*$/\n&\n/;tx;:x;s/\(\n.\)\(.*\)\(.\n\)/\3\2\1/;tx;s/\n//g;s/\(.\)/\1./g;s/$/ip6.arpa/')/][::]:553'" >>"${YAML_ORI}" PTXT " - '[/router.asus.com/][::]:553'" \ " - '[/www.asusnetwork.net/][::]:553'" \ " - '[/www.asusrouter.com/][::]:553'" \ " - '[/use-application-dns.net/][::]:553'" \ " - '[/dns.resolver.arpa/][::]:553'" \ " - '[/${DOMAIN}/][::]:553'" \ " - '[//][::]:553'" \ " - ${BOOTSTRAP1}" \ " - ${BOOTSTRAP2}" \ " - tcp://${BOOTSTRAP1}" \ " - tcp://${BOOTSTRAP2}" \ " bootstrap_dns:" \ " - ${BOOTSTRAP1}" \ " - ${BOOTSTRAP2}" \ " fallback_dns:" \ " - ${BOOTSTRAP1}" \ " - ${BOOTSTRAP2}" \ " - tcp://${BOOTSTRAP1}" \ " - tcp://${BOOTSTRAP2}" \ " resolve_clients: true" \ " use_private_ptr_resolvers: true" \ " local_ptr_upstreams:" \ " - '[::]:553'" \ " - '[/10.in-addr.arpa/][::]:553'" \ " - '[/$(printf "%s\n" "${NET_ADDR}" | awk 'BEGIN{FS="."}{print $2"."$1".in-addr.arpa"}')/][::]:553'" \ "schema_version: ${SCHEMA_VER}" >>"${YAML_ORI}" PTXT "${INFO} Writing AdGuardHome configuration..." cp -f "${YAML_ORI}" "${YAML_FILE}" fi if ! check_AdGuardHome_yaml; then PTXT "${ERROR} Writing AdGuardHome configuration failed " \ "${ERROR} Please send ${YAML_ERR} file and screen log of " \ "${ERROR} all ssh terminal operations to the script developer." return 1 fi ;; esac fi } yaml_nvars_insert() { PATTERN="$(_quote "$1")" CONTENT="$(_quote "$2")" sed -i "/${PATTERN}/a${CONTENT}" "$3" } yaml_nvars_replace() { PATTERN="$(_quote "$1")" CONTENT="$(_quote "$2")" sed -i "s/${PATTERN}/${CONTENT}/" "$3" } yaml_nvars_append() { echo "$1" >>"$2" } yaml_nvars_delete() { PATTERN="$(_quote "$1")" sed -i "/${PATTERN}/d" "$2" } uninst_all() { if [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; then PTXT "${INFO} Old Backup Detected!" if read_yesno "Do you want to remove backup?(this will prevent restoring from backups later)"; then rm -rf "${BASE_DIR}/backup_AdGuardHome.tar.gz"; else PTXT "${INFO} Keeping backup instead."; fi fi (if [ -n "$(pidof AdGuardHome)" ]; then { if { service stop_AdGuardHome >/dev/null 2>&1; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome stop; }; then sleep 1s; elif { /opt/etc/init.d/S99AdGuardHome kill; }; then sleep 1s; else { killall -q -9 AdGuardHome 2>/dev/null; } rm -rf /opt/var/run/AdGuardHome.pid 2>/dev/null sleep 1s fi; }; fi) & local PID="$!" wait "${PID}" 2>/dev/null ({ until [ "$(pidof AdGuardHome S99AdGuardHome | wc -w)" -lt "1" ]; do sleep 1s; done; }) & local PID="$!" wait "${PID}" 2>/dev/null sleep 1s { /opt/etc/init.d/S99AdGuardHome check; } [ "$(awk -F'=' '/ADGUARD_DOMAIN/ {print $2}' "${CONF_FILE}" | sed -e 's/^"//' -e 's/"$//')" = "yes" ] && nvram set lan_domain="" && nvram commit mv "${TARG_DIR}/installer" "${HOME}/installer" rm -rf "${TARG_DIR}" "/opt/etc/init.d/S99AdGuardHome" "/opt/etc/init.d/rc.func.AdGuardHome" "/opt/sbin/AdGuardHome" "/opt/bin/bcrypt-tool" "${ADDON_DIR}" "/opt/var/log/AdGuardHome.log" yaml_nvars_delete "#Asuswrt-Merlin AdGuardHome Installer" /jffs/scripts/dnsmasq.postconf del_jffs_script /jffs/scripts/init-start del_jffs_script /jffs/scripts/dnsmasq.postconf del_jffs_script /jffs/scripts/services-stop del_between_magic /jffs/scripts/service-event-end '# Asuswrt-Merlin-AdGuardHome-Installer' service restart_dnsmasq >/dev/null 2>&1 end_op_message 0 } write_conf() { local VAR VALUE VAR="$1" VALUE="$2" if [ ! -f "${CONF_FILE}" ]; then touch "${CONF_FILE}" && chmod 644 "${CONF_FILE}" fi if grep -q "${VAR}" "${CONF_FILE}"; then VALUE=$(_quote "${VALUE}") sed -i "/^${VAR}=/s/=.*/=${VALUE}/" "${CONF_FILE}" else PTXT "${VAR}=${VALUE}" >>"${CONF_FILE}" fi } write_command_script() { local TARG COMMAND FILENAME TARG="$1" COMMAND="$2" FILENAME="$(basename "${TARG}")" if [ ! -f "${TARG}" ]; then PTXT "${INFO} Creating ${FILENAME} file" PTXT "#!/bin/sh" >"${TARG}" fi chmod 755 "${TARG}" if [ "$(grep -c -F "${COMMAND}" "${TARG}")" -gt 0 ]; then PTXT "${INFO} ${FILENAME} file already configured" else PTXT "${INFO} Configure ${FILENAME} file" PTXT "${COMMAND}" >>"${TARG}" fi } write_manager_script() { local TARG OP FILENAME COMMAND TARG="$1" OP="$2" FILENAME="$(basename "${TARG}")" COMMAND="${ADDON_DIR}/AdGuardHome.sh" if [ ! -f "${TARG}" ]; then PTXT "${INFO} Creating ${FILENAME} file" PTXT "#!/bin/sh" >"${TARG}" fi chmod 755 "${TARG}" "${COMMAND}" del_between_magic "${TARG}" Asuswrt-Merlin-AdGuardHome-Installer if [ "$(grep -c -F "[ -x ${COMMAND} ] && ${COMMAND} ${OP}" "${TARG}")" -gt 0 ]; then PTXT "${INFO} ${FILENAME} file already configured" else PTXT "${INFO} Configure ${FILENAME} file" if grep -q "^${COMMAND}" "${TARG}"; then sed -i "s~^${COMMAND}~[ -x ${COMMAND} ] \&\& ${COMMAND} ${OP}~" "${TARG}" else del_jffs_script "${TARG}" !manager [ "$(tail -1 "${TARG}" | grep -c '^$')" -eq 0 ] && PTXT "" >>"${TARG}" PTXT "[ -x ${COMMAND} ] && ${COMMAND} ${OP}" >>"${TARG}" fi fi } [ "$1" ] && BRANCH="$1" || BRANCH="master" [ -z "$(nvram get odmpid)" ] && ROUTER_MODEL="$(nvram get productid)" || ROUTER_MODEL="$(nvram get odmpid)" RURL="https://raw.githubusercontent.com/jumpsmm7/Asuswrt-Merlin-AdGuardHome-Installer/${BRANCH}" ROUTER_OS="$(/bin/uname)" ! PTXT "${ROUTER_MODEL}" | grep -vE "RT-AC(86|2900)" | grep -q "AC" && ROUTER_ARCH="$(/bin/uname -m)" || ROUTER_ARCH="armv7" # Exception for version ARMv5 NAT_ENV="$(nvram get wan_ipaddr | grep -oE '\b^(((10|127)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})|(((172\.(1[6-9]|2[0-9]|3[0-1]))|(192\.168))(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){2}))$\b')" [ -z "${ROUTER_MODEL}" ] && PTXT "${ERROR} This is an unsupported router, sorry." && sleep 3s && exit 1 [ "$(nvram get sw_mode)" != "1" ] && PTXT "${ERROR} You are not running in router mode, sorry." && sleep 3s && exit 1 if [ -n "${NAT_ENV}" ] && [ -z "$2" ]; then PTXT "${WARNING} This router is in a Double-Nat Environment. While for some users of AdGuardHome this may work fine," \ "${WARNING} Consider this a warning that there is no guarentee that it will." \ "${WARNING} The script developer cannot help in this scenario." \ "${WARNING} However you are still welcome to use the script." sleep 3s fi if [ -z "$2" ]; then printf '\e[8;50;125t' printf '\033[?7l' clear sed -n '2,21p' "$0" printf '\033[?7h' fi case "${ROUTER_MODEL}" in *) [ -z "$2" ] && PTXT "${INFO} Detected ${ROUTER_MODEL} router." ;; esac case "${ROUTER_OS}" in "Linux") [ -z "$2" ] && PTXT "${INFO} Detected ${ROUTER_OS} platform." ROUTER_OS="linux" ;; *) PTXT "${ERROR} This is an unsupported platform, sorry." exit 1 ;; esac case "${ROUTER_ARCH}" in "aarch64" | "arm64") ROUTER_ARCH="arm64" ADGUARD_ARCH="${ROUTER_OS}_${ROUTER_ARCH}" [ -z "$2" ] && PTXT "${INFO} Detected ARMv8 architecture." ;; "armv7l") ROUTER_ARCH="armv7" ADGUARD_ARCH="${ROUTER_OS}_${ROUTER_ARCH}" [ -z "$2" ] && PTXT "${INFO} Detected ARMv7 architecture." ;; "armv7") ROUTER_ARCH="armv5" ADGUARD_ARCH="${ROUTER_OS}_${ROUTER_ARCH}" [ -z "$2" ] && PTXT "${INFO} Detected ARMv7 architecture." ;; *) PTXT "${ERROR} This is an unsupported architecture, sorry." exit 1 ;; esac menu() { trap - HUP INT QUIT ABRT TERM case "$1" in "") PTXT "${INFO} Choose what you want to do:" \ " 1) Install/Update AdGuardHome" \ " 2) Uninstall" if { [ -d "${TARG_DIR}" ] && [ -f "${AGH_FILE}" ]; }; then { PTXT " 3) Change Username/Password" " 4) Reconfigure" " b) Backup"; }; fi if { [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; }; then { PTXT " r) Restore"; }; fi PTXT " q) Quit" if { [ ! -d "${TARG_DIR}" ] || [ ! -f "${AGH_FILE}" ]; } && [ ! -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; then { read_input_num "Please enter the number that designates your selection:" 1 2 q; }; fi if { [ ! -d "${TARG_DIR}" ] || [ ! -f "${AGH_FILE}" ]; } && [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; then { read_input_num "Please enter the number that designates your selection:" 1 2 r q; }; fi if { [ -d "${TARG_DIR}" ] && [ -f "${AGH_FILE}" ] && [ ! -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; }; then { read_input_num "Please enter the number that designates your selection:" 1 4 b q; }; fi if { [ -d "${TARG_DIR}" ] && [ -f "${AGH_FILE}" ] && [ -f "${BASE_DIR}/backup_AdGuardHome.tar.gz" ]; }; then { read_input_num "Please enter the number that designates your selection:" 1 4 b r q; }; fi ;; *) CHOSEN="$1" ;; esac [ -n "${CHOSEN}" ] && trap 'clear; end_op_message 2' HUP INT QUIT ABRT TERM case "${CHOSEN}" in "1" | "install" | "update") [ "${CHOSEN}" = "update" ] && AUTO_UPDATE="update" PTXT "${INFO} This operation will install AdGuardHome and related files (<6MB)" \ "${INFO} to ENTWARE, no other data will be changed." \ "${INFO} Also some start scripts will be installed/modified as required." if read_yesno "Do you want to install AdGuardHome?"; then inst_AdGuardHome "${AUTO_UPDATE:-install}"; else end_op_message 1; fi ;; "2" | "uninstall") PTXT "${INFO} This operation will cleanup everything installed by this script." if read_yesno "Do you want to continue?"; then uninst_all; else end_op_message 1; fi ;; "3" | "changepw") PTXT "${INFO} This operation will allow you to change your username and password." if read_yesno "Do you want to continue?"; then AdGuardHome_authen 0; else end_op_message 1; fi ;; "4" | "reconfigure") PTXT "${INFO} This operation allows you to configure AdGuardHome" if read_yesno "Do you want to proceed?"; then setup_AdGuardHome reconfig; else end_op_message 1; fi ;; "b" | "B" | "backup") PTXT "${INFO} This operation will backup everything!" if read_yesno "Do you want to continue?"; then backup_restore BACKUP; else end_op_message 1; fi ;; "r" | "R" | "restore") PTXT "${INFO} This operation will restore everything!" if read_yesno "Do you want to continue?"; then backup_restore RESTORE; else end_op_message 1; fi ;; "q" | "Q") PTXT "${INFO} Operations have been applied if any has been made" \ "${INFO} In case of anomaly, please reboot your router!" if [ -f "${HOME}/installer" ]; then rm -rf "${HOME}/installer"; fi sleep 3s clear ;; esac } case "$2" in "") cleanup check_jffs_enabled check_dns_environment check_version menu ;; [1-4] | "install" | "update" | "uninstall" | "changepw" | "reconfigure" | [bB] | "backup" | [rR] | "restore") menu "$2" ;; *) PTXT "${INFO} Usage: sh installer {master|dev?|""} (1 - {install/update} | 2 - uninstall | 3 - changepw | 4 - reconfigure | {b/B} - backup | {r/R} - restore)" \ "${INFO} Branch or Tag must be represented by a value or empty quotes in place of string e.g. \"\" in the \$1 string position." \ "${INFO} Action must be represented by a value (1 - {install/update} | 2 - uninstall | 3 - changepw | 4 - reconfigure | {b/B} - backup | {r/R} - restore) in the \$2 string position." \ "${INFO} An update example sh installer master update, or just use the regular menu by not specifying an Action." ;; esac