#!/bin/sh ############################################################## ## ## ## ___ ___ _ __ _ __ _ __ ___ ___ _ __ ## ## / __| / _ \ | '_ \ | '_ \ | '_ ` _ \ / _ \ | '_ \ ## ## | (__ | (_) || | | || | | || | | | | || (_) || | | | ## ## \___| \___/ |_| |_||_| |_||_| |_| |_| \___/ |_| |_| ## ## ## ## https://github.com/jackyaz/connmon ## ## ## ############################################################## ############## Shellcheck directives ############# # shellcheck disable=SC1090 # shellcheck disable=SC2009 # shellcheck disable=SC2012 # shellcheck disable=SC2016 # shellcheck disable=SC2018 # shellcheck disable=SC2019 # shellcheck disable=SC2039 # shellcheck disable=SC2059 # shellcheck disable=SC2086 # shellcheck disable=SC2155 # shellcheck disable=SC2181 # shellcheck disable=SC3003 ############################################################## ### Start of script variables ### readonly SCRIPT_NAME="connmon" readonly SCRIPT_VERSION="v3.0.2" SCRIPT_BRANCH="master" SCRIPT_REPO="https://jackyaz.io/$SCRIPT_NAME/$SCRIPT_BRANCH" readonly SCRIPT_DIR="/jffs/addons/$SCRIPT_NAME.d" readonly SCRIPT_WEBPAGE_DIR="$(readlink /www/user)" readonly SCRIPT_WEB_DIR="$SCRIPT_WEBPAGE_DIR/$SCRIPT_NAME" readonly SHARED_DIR="/jffs/addons/shared-jy" readonly SHARED_REPO="https://jackyaz.io/shared-jy/master" readonly SHARED_WEB_DIR="$SCRIPT_WEBPAGE_DIR/shared-jy" readonly EMAIL_DIR="/jffs/addons/amtm/mail" readonly EMAIL_CONF="$EMAIL_DIR/email.conf" readonly EMAIL_REGEX="^(([A-Za-z0-9]+((\.|\-|\_|\+)?[A-Za-z0-9]?)*[A-Za-z0-9]+)|[A-Za-z0-9]+)@(([A-Za-z0-9]+)+((\.|\-|\_)?([A-Za-z0-9]+)+)*)+\.([A-Za-z]{2,})+$" [ -z "$(nvram get odmpid)" ] && ROUTER_MODEL="$(nvram get productid)" || ROUTER_MODEL="$(nvram get odmpid)" [ -f /opt/bin/sqlite3 ] && SQLITE3_PATH=/opt/bin/sqlite3 || SQLITE3_PATH=/usr/sbin/sqlite3 ### End of script variables ### ### Start of output format variables ### readonly CRIT="\\e[41m" readonly ERR="\\e[31m" readonly WARN="\\e[33m" readonly PASS="\\e[32m" readonly BOLD="\\e[1m" readonly SETTING="${BOLD}\\e[36m" readonly UNDERLINE="\\e[4m" readonly CLEARFORMAT="\\e[0m" ### End of output format variables ### # $1 = print to syslog, $2 = message to print, $3 = log level Print_Output(){ if [ "$1" = "true" ]; then logger -t "$SCRIPT_NAME" "$2" fi printf "${BOLD}${3}%s${CLEARFORMAT}\\n\\n" "$2" } Firmware_Version_Check(){ if nvram get rc_support | grep -qF "am_addons"; then return 0 else return 1 fi } ### Code for these functions inspired by https://github.com/Adamm00 - credit to @Adamm ### Check_Lock(){ if [ -f "/tmp/$SCRIPT_NAME.lock" ]; then ageoflock="$(($(/bin/date "+%s") - $(/bin/date "+%s" -r "/tmp/$SCRIPT_NAME.lock")))" if [ "$ageoflock" -gt 600 ]; then Print_Output true "Stale lock file found (>600 seconds old) - purging lock" "$ERR" kill "$(sed -n '1p' "/tmp/$SCRIPT_NAME.lock")" >/dev/null 2>&1 Clear_Lock echo "$$" > "/tmp/$SCRIPT_NAME.lock" return 0 else Print_Output true "Lock file found (age: $ageoflock seconds) - ping test likely currently running" "$ERR" if [ -z "$1" ]; then exit 1 else if [ "$1" = "webui" ]; then echo 'var connmonstatus = "LOCKED";' > "$SCRIPT_WEB_DIR/detect_connmon.js" exit 1 fi return 1 fi fi else echo "$$" > "/tmp/$SCRIPT_NAME.lock" return 0 fi } Clear_Lock(){ rm -f "/tmp/$SCRIPT_NAME.lock" 2>/dev/null return 0 } ############################################################################ Set_Version_Custom_Settings(){ SETTINGSFILE="/jffs/addons/custom_settings.txt" case "$1" in local) if [ -f "$SETTINGSFILE" ]; then if [ "$(grep -c "connmon_version_local" $SETTINGSFILE)" -gt 0 ]; then if [ "$2" != "$(grep "connmon_version_local" /jffs/addons/custom_settings.txt | cut -f2 -d' ')" ]; then sed -i "s/connmon_version_local.*/connmon_version_local $2/" "$SETTINGSFILE" fi else echo "connmon_version_local $2" >> "$SETTINGSFILE" fi else echo "connmon_version_local $2" >> "$SETTINGSFILE" fi ;; server) if [ -f "$SETTINGSFILE" ]; then if [ "$(grep -c "connmon_version_server" $SETTINGSFILE)" -gt 0 ]; then if [ "$2" != "$(grep "connmon_version_server" /jffs/addons/custom_settings.txt | cut -f2 -d' ')" ]; then sed -i "s/connmon_version_server.*/connmon_version_server $2/" "$SETTINGSFILE" fi else echo "connmon_version_server $2" >> "$SETTINGSFILE" fi else echo "connmon_version_server $2" >> "$SETTINGSFILE" fi ;; esac } Update_Check(){ echo 'var updatestatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_update.js" doupdate="false" localver="$(grep "SCRIPT_VERSION=" "/jffs/scripts/$SCRIPT_NAME" | grep -m1 -oE 'v[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})')" Set_Version_Custom_Settings local "$localver" /usr/sbin/curl -fsL --retry 3 --connect-timeout 15 "$SCRIPT_REPO/404/$SCRIPT_NAME.sh" | grep -qF "jackyaz" || { Print_Output true "404 error detected - stopping update" "$ERR"; return 1; } serverver="$(/usr/sbin/curl -fsL --retry 3 --connect-timeout 15 "$SCRIPT_REPO/version/$SCRIPT_NAME.sh" | grep "SCRIPT_VERSION=" | grep -m1 -oE 'v[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})')" if [ "$localver" != "$serverver" ]; then doupdate="version" Set_Version_Custom_Settings server "$serverver" changelog="$(/usr/sbin/curl -fsL --retry 3 --connect-timeout 15 "$SCRIPT_REPO/files/CHANGELOG.md" | sed -n "/$serverver"'/,/##/p' | head -n -1 | sed 's/## //')" echo 'var changelog = "
Changelog
'"$(echo "$changelog" | tr '\n' '|' | sed 's/|/
/g')"'
"' > "$SCRIPT_WEB_DIR/detect_changelog.js" echo 'var updatestatus = "'"$serverver"'";' > "$SCRIPT_WEB_DIR/detect_update.js" else localmd5="$(md5sum "/jffs/scripts/$SCRIPT_NAME" | awk '{print $1}')" remotemd5="$(curl -fsL --retry 3 "$SCRIPT_REPO/md5/$SCRIPT_NAME.sh" | md5sum | awk '{print $1}')" if [ "$localmd5" != "$remotemd5" ]; then doupdate="md5" Set_Version_Custom_Settings server "$serverver-hotfix" echo 'var updatestatus = "'"$serverver-hotfix"'";' > "$SCRIPT_WEB_DIR/detect_update.js" fi fi if [ "$doupdate" = "false" ]; then echo 'var updatestatus = "None";' > "$SCRIPT_WEB_DIR/detect_update.js" fi echo "$doupdate,$localver,$serverver" } Update_Version(){ if [ -z "$1" ]; then updatecheckresult="$(Update_Check)" isupdate="$(echo "$updatecheckresult" | cut -f1 -d',')" localver="$(echo "$updatecheckresult" | cut -f2 -d',')" serverver="$(echo "$updatecheckresult" | cut -f3 -d',')" if [ "$isupdate" = "version" ]; then Print_Output true "New version of $SCRIPT_NAME available - $serverver" "$PASS" changelog="$(/usr/sbin/curl -fsL --retry 3 --connect-timeout 15 "$SCRIPT_REPO/files/CHANGELOG.md" | sed -n "/$serverver"'/,/##/p' | head -n -1 | sed 's/## //')" printf "${BOLD}${UNDERLINE}Changelog\\n${CLEARFORMAT}%s\\n\\n" "$changelog" elif [ "$isupdate" = "md5" ]; then Print_Output true "MD5 hash of $SCRIPT_NAME does not match - hotfix available - $serverver" "$PASS" fi if [ "$isupdate" != "false" ]; then printf "\\n${BOLD}Do you want to continue with the update? (y/n)${CLEARFORMAT} " read -r confirm case "$confirm" in y|Y) printf "\\n" Update_File CHANGELOG.md Update_File README.md Update_File LICENSE Update_File shared-jy.tar.gz Update_File connmonstats_www.asp Download_File "$SCRIPT_REPO/update/$SCRIPT_NAME.sh" "/jffs/scripts/$SCRIPT_NAME" && Print_Output true "$SCRIPT_NAME successfully updated" chmod 0755 "/jffs/scripts/$SCRIPT_NAME" Set_Version_Custom_Settings local "$serverver" Set_Version_Custom_Settings server "$serverver" Clear_Lock PressEnter exec "$0" exit 0 ;; *) printf "\\n" Clear_Lock return 1 ;; esac else Print_Output true "No updates available - latest is $localver" "$WARN" Clear_Lock fi fi if [ "$1" = "force" ]; then serverver="$(/usr/sbin/curl -fsL --retry 3 --connect-timeout 15 "$SCRIPT_REPO/version/$SCRIPT_NAME.sh" | grep "SCRIPT_VERSION=" | grep -m1 -oE 'v[0-9]{1,2}([.][0-9]{1,2})([.][0-9]{1,2})')" Print_Output true "Downloading latest version ($serverver) of $SCRIPT_NAME" "$PASS" Update_File CHANGELOG.md Update_File README.md Update_File LICENSE Update_File shared-jy.tar.gz Update_File connmonstats_www.asp Download_File "$SCRIPT_REPO/update/$SCRIPT_NAME.sh" "/jffs/scripts/$SCRIPT_NAME" && Print_Output true "$SCRIPT_NAME successfully updated" chmod 0755 "/jffs/scripts/$SCRIPT_NAME" Set_Version_Custom_Settings local "$serverver" Set_Version_Custom_Settings server "$serverver" Clear_Lock if [ -z "$2" ]; then PressEnter exec "$0" elif [ "$2" = "unattended" ]; then exec "$0" postupdate fi exit 0 fi } Update_File(){ if [ "$1" = "connmonstats_www.asp" ]; then tmpfile="/tmp/$1" Download_File "$SCRIPT_REPO/files/$1" "$tmpfile" if ! diff -q "$tmpfile" "$SCRIPT_DIR/$1" >/dev/null 2>&1; then if [ -f "$SCRIPT_DIR/$1" ]; then Get_WebUI_Page "$SCRIPT_DIR/$1" sed -i "\\~$MyPage~d" /tmp/menuTree.js rm -f "$SCRIPT_WEBPAGE_DIR/$MyPage" 2>/dev/null fi Download_File "$SCRIPT_REPO/files/$1" "$SCRIPT_DIR/$1" Print_Output true "New version of $1 downloaded" "$PASS" Mount_WebUI fi rm -f "$tmpfile" elif [ "$1" = "shared-jy.tar.gz" ]; then if [ ! -f "$SHARED_DIR/$1.md5" ]; then Download_File "$SHARED_REPO/$1" "$SHARED_DIR/$1" Download_File "$SHARED_REPO/$1.md5" "$SHARED_DIR/$1.md5" tar -xzf "$SHARED_DIR/$1" -C "$SHARED_DIR" rm -f "$SHARED_DIR/$1" Print_Output true "New version of $1 downloaded" "$PASS" else localmd5="$(cat "$SHARED_DIR/$1.md5")" remotemd5="$(curl -fsL --retry 3 "$SHARED_REPO/$1.md5")" if [ "$localmd5" != "$remotemd5" ]; then Download_File "$SHARED_REPO/$1" "$SHARED_DIR/$1" Download_File "$SHARED_REPO/$1.md5" "$SHARED_DIR/$1.md5" tar -xzf "$SHARED_DIR/$1" -C "$SHARED_DIR" rm -f "$SHARED_DIR/$1" Print_Output true "New version of $1 downloaded" "$PASS" fi fi elif [ "$1" = "CHANGELOG.md" ]; then tmpfile="/tmp/$1" Download_File "$SCRIPT_REPO/files/$1" "$tmpfile" if ! diff -q "$tmpfile" "$SCRIPT_DIR/$1" >/dev/null 2>&1; then Download_File "$SCRIPT_REPO/files/$1" "$SCRIPT_DIR/$1" fi rm -f "$tmpfile" elif [ "$1" = "README.md" ] || [ "$1" = "LICENSE" ]; then tmpfile="/tmp/$1" Download_File "$SCRIPT_REPO/files/$1" "$tmpfile" if ! diff -q "$tmpfile" "$SCRIPT_DIR/$1" >/dev/null 2>&1; then Download_File "$SCRIPT_REPO/files/$1" "$SCRIPT_DIR/$1" fi rm -f "$tmpfile" else return 1 fi } Validate_Number(){ if [ "$1" -eq "$1" ] 2>/dev/null; then return 0 else return 1 fi } Validate_IP(){ if expr "$1" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null; then for i in 1 2 3 4; do if [ "$(echo "$1" | cut -d. -f$i)" -gt 255 ]; then Print_Output false "Octet $i ($(echo "$1" | cut -d. -f$i)) - is invalid, must be less than 255" "$ERR" return 1 fi done else Print_Output false "$1 - is not a valid IPv4 address, valid format is 1.2.3.4" "$ERR" return 1 fi } Validate_Domain(){ if ! nslookup "$1" >/dev/null 2>&1; then Print_Output false "$1 cannot be resolved by nslookup, please ensure you enter a valid domain name" "$ERR" return 1 else return 0 fi } Conf_FromSettings(){ SETTINGSFILE="/jffs/addons/custom_settings.txt" TMPFILE="/tmp/connmon_settings.txt" if [ -f "$SETTINGSFILE" ]; then if [ "$(grep "connmon_" $SETTINGSFILE | grep -v "version" -c)" -gt 0 ]; then Print_Output true "Updated settings from WebUI found, merging into $SCRIPT_CONF" "$PASS" cp -a "$SCRIPT_CONF" "$SCRIPT_CONF.bak" grep "connmon_" "$SETTINGSFILE" | grep -v "version" > "$TMPFILE" sed -i "s/connmon_//g;s/ /=/g" "$TMPFILE" while IFS='' read -r line || [ -n "$line" ]; do SETTINGNAME="$(echo "$line" | cut -f1 -d'=' | awk '{print toupper($1)}')" SETTINGVALUE="$(echo "$line" | cut -f2 -d'=')" if [ "$SETTINGNAME" = "NOTIFICATIONS_PUSHOVER_LIST" ] || [ "$SETTINGNAME" = "NOTIFICATIONS_WEBHOOK_LIST" ] || [ "$SETTINGNAME" = "NOTIFICATIONS_EMAIL_LIST" ]; then SETTINGVALUE="$(echo "$SETTINGVALUE" | sed 's~||||~,~g')" fi sed -i "s~$SETTINGNAME=.*~$SETTINGNAME=$SETTINGVALUE~" "$SCRIPT_CONF" done < "$TMPFILE" grep 'connmon_version' "$SETTINGSFILE" > "$TMPFILE" sed -i "\\~connmon_~d" "$SETTINGSFILE" mv "$SETTINGSFILE" "$SETTINGSFILE.bak" cat "$SETTINGSFILE.bak" "$TMPFILE" > "$SETTINGSFILE" rm -f "$TMPFILE" rm -f "$SETTINGSFILE.bak" ScriptStorageLocation "$(ScriptStorageLocation check)" Create_Symlinks if AutomaticMode check; then if diff "$SCRIPT_CONF" "$SCRIPT_CONF.bak" | grep -q "^SCH"; then Auto_Cron delete 2>/dev/null Auto_Cron create 2>/dev/null fi else Auto_Cron delete 2>/dev/null fi if diff "$SCRIPT_CONF" "$SCRIPT_CONF.bak" | grep -q "OUTPUTTIMEMODE=\|DAYSTOKEEP=\|LASTXRESULTS="; then Generate_CSVs fi Print_Output true "Merge of updated settings from WebUI completed successfully" "$PASS" else Print_Output false "No updated settings from WebUI found, no merge into $SCRIPT_CONF necessary" "$PASS" fi fi } EmailConf_FromSettings(){ SETTINGSFILE="/jffs/addons/custom_settings.txt" TMPFILE="/tmp/email_settings.txt" if [ -f "$SETTINGSFILE" ]; then Print_Output true "Updated email settings from WebUI found, merging into $EMAIL_CONF" "$PASS" cp -a "$EMAIL_CONF" "$EMAIL_CONF.bak" grep "email_" "$SETTINGSFILE" > "$TMPFILE" sed -i "s/email_//g;s/ /=/g" "$TMPFILE" while IFS='' read -r line || [ -n "$line" ]; do SETTINGNAME="$(echo "$line" | cut -f1 -d'=' | awk '{print toupper($1)}')" SETTINGVALUE="$(echo "$line" | cut -f2- -d'=' | sed 's/=/ /g')" if [ "$SETTINGNAME" = "PASSWORD" ]; then Email_Encrypt_Password "$SETTINGVALUE" else sed -i "s~$SETTINGNAME=.*~$SETTINGNAME=\"$SETTINGVALUE\"~" "$EMAIL_CONF" fi done < "$TMPFILE" sed -i "\\~email_~d" "$SETTINGSFILE" rm -f "$TMPFILE" Print_Output true "Merge of updated email settings from WebUI completed successfully" "$PASS" fi } Create_Dirs(){ if [ ! -d "$SCRIPT_DIR" ]; then mkdir -p "$SCRIPT_DIR" fi if [ ! -d "$SCRIPT_STORAGE_DIR" ]; then mkdir -p "$SCRIPT_STORAGE_DIR" fi if [ ! -d "$CSV_OUTPUT_DIR" ]; then mkdir -p "$CSV_OUTPUT_DIR" fi if [ ! -d "$SHARED_DIR" ]; then mkdir -p "$SHARED_DIR" fi if [ ! -d "$SCRIPT_WEBPAGE_DIR" ]; then mkdir -p "$SCRIPT_WEBPAGE_DIR" fi if [ ! -d "$SCRIPT_WEB_DIR" ]; then mkdir -p "$SCRIPT_WEB_DIR" fi if [ ! -d "$EMAIL_DIR" ]; then mkdir -p "$EMAIL_DIR" fi if [ ! -d "$USER_SCRIPT_DIR" ]; then mkdir -p "$USER_SCRIPT_DIR" fi } Create_Symlinks(){ ln -sf "$SCRIPT_STORAGE_DIR/connstatstext.js" "$SCRIPT_WEB_DIR/connstatstext.js" 2>/dev/null ln -sf "$SCRIPT_STORAGE_DIR/lastx.csv" "$SCRIPT_WEB_DIR/lastx.htm" 2>/dev/null ln -sf "$EMAIL_CONF" "$SCRIPT_WEB_DIR/email_config.htm" 2>/dev/null ln -sf "$SCRIPT_CONF" "$SCRIPT_WEB_DIR/config.htm" 2>/dev/null ln -sf "$SCRIPT_DIR/CHANGELOG.md" "$SCRIPT_WEB_DIR/changelog.htm" 2>/dev/null ln -sf "$SCRIPT_STORAGE_DIR/.cron" "$SCRIPT_WEB_DIR/cron.js" 2>/dev/null ln -sf "$SCRIPT_STORAGE_DIR/.customactioninfo" "$SCRIPT_WEB_DIR/customactioninfo.htm" 2>/dev/null ln -sf "$SCRIPT_STORAGE_DIR/.customactionlist" "$SCRIPT_WEB_DIR/customactionlist.htm" 2>/dev/null ln -sf "$SCRIPT_STORAGE_DIR/.emailinfo" "$SCRIPT_WEB_DIR/emailinfo.htm" 2>/dev/null ln -sf "$CSV_OUTPUT_DIR" "$SCRIPT_WEB_DIR/csv" 2>/dev/null if [ ! -d "$SHARED_WEB_DIR" ]; then ln -s "$SHARED_DIR" "$SHARED_WEB_DIR" 2>/dev/null fi } Conf_Exists(){ if [ -f "$SCRIPT_CONF" ]; then dos2unix "$SCRIPT_CONF" chmod 0644 "$SCRIPT_CONF" sed -i -e 's/"//g' "$SCRIPT_CONF" if grep -q "SCHEDULESTART" "$SCRIPT_CONF"; then if ! grep -q "AUTOMATED" "$SCRIPT_CONF"; then echo "AUTOMATED=true" >> "$SCRIPT_CONF" fi if ! grep -q "SCHDAYS" "$SCRIPT_CONF"; then echo "SCHDAYS=*" >> "$SCRIPT_CONF" fi echo "SCHHOURS=*" >> "$SCRIPT_CONF" PINGFREQUENCY="$(Conf_Parameters check PINGFREQUENCY)" echo "SCHMINS=*/$PINGFREQUENCY" >> "$SCRIPT_CONF" sed -i '/SCHEDULESTART/d;/SCHEDULEEND/d;/PINGFREQUENCY/d;' "$SCRIPT_CONF" fi if grep -q "OUTPUTDATAMODE" "$SCRIPT_CONF"; then sed -i '/OUTPUTDATAMODE/d;' "$SCRIPT_CONF" fi if ! grep -q "DAYSTOKEEP" "$SCRIPT_CONF"; then echo "DAYSTOKEEP=30" >> "$SCRIPT_CONF" fi if ! grep -q "LASTXRESULTS" "$SCRIPT_CONF"; then echo "LASTXRESULTS=10" >> "$SCRIPT_CONF" fi if ! grep -q "EXCLUDEFROMQOS" "$SCRIPT_CONF"; then echo "EXCLUDEFROMQOS=true" >> "$SCRIPT_CONF" fi if ! grep -q "NOTIFICATIONS" "$SCRIPT_CONF"; then { echo "NOTIFICATIONS_EMAIL=false" echo "NOTIFICATIONS_WEBHOOK=false" echo "NOTIFICATIONS_PUSHOVER=false" echo "NOTIFICATIONS_CUSTOM=false" echo "NOTIFICATIONS_HEALTHCHECK=false" echo "NOTIFICATIONS_INFLUXDB=false" echo "NOTIFICATIONS_PINGTEST=None" echo "NOTIFICATIONS_PINGTHRESHOLD=None" echo "NOTIFICATIONS_JITTERTHRESHOLD=None" echo "NOTIFICATIONS_LINEQUALITYTHRESHOLD=None" echo "NOTIFICATIONS_PINGTHRESHOLD_VALUE=30" echo "NOTIFICATIONS_JITTERTHRESHOLD_VALUE=15" echo "NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE=75" echo "NOTIFICATIONS_EMAIL_LIST=" echo "NOTIFICATIONS_HEALTHCHECK_UUID=" echo "NOTIFICATIONS_WEBHOOK_LIST=" echo "NOTIFICATIONS_PUSHOVER_LIST=" echo "NOTIFICATIONS_PUSHOVER_API=" echo "NOTIFICATIONS_PUSHOVER_USERKEY=" echo "NOTIFICATIONS_INFLUXDB_HOST=" echo "NOTIFICATIONS_INFLUXDB_PORT=8086" echo "NOTIFICATIONS_INFLUXDB_DB=connmon" echo "NOTIFICATIONS_INFLUXDB_VERSION=1.8" echo "NOTIFICATIONS_INFLUXDB_USERNAME=" echo "NOTIFICATIONS_INFLUXDB_PASSWORD=" echo "NOTIFICATIONS_INFLUXDB_APITOKEN=" } >> "$SCRIPT_CONF" fi return 0 else { echo "PINGSERVER=8.8.8.8"; echo "OUTPUTTIMEMODE=unix"; echo "STORAGELOCATION=jffs"; echo "PINGDURATION=60"; echo "AUTOMATED=true"; echo "SCHDAYS=*"; echo "SCHHOURS=*"; echo "SCHMINS=*/3"; echo "DAYSTOKEEP=30"; echo "LASTXRESULTS=10"; echo "EXCLUDEFROMQOS=true"; echo "NOTIFICATIONS_EMAIL=false"; echo "NOTIFICATIONS_WEBHOOK=false"; echo "NOTIFICATIONS_PUSHOVER=false"; echo "NOTIFICATIONS_CUSTOM=false"; echo "NOTIFICATIONS_HEALTHCHECK=false"; echo "NOTIFICATIONS_INFLUXDB=false"; echo "NOTIFICATIONS_PINGTEST=None"; echo "NOTIFICATIONS_PINGTHRESHOLD=None"; echo "NOTIFICATIONS_JITTERTHRESHOLD=None"; echo "NOTIFICATIONS_LINEQUALITYTHRESHOLD=None"; echo "NOTIFICATIONS_PINGTHRESHOLD_VALUE=30"; echo "NOTIFICATIONS_JITTERTHRESHOLD_VALUE=15"; echo "NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE=75"; echo "NOTIFICATIONS_EMAIL_LIST="; echo "NOTIFICATIONS_HEALTHCHECK_UUID="; echo "NOTIFICATIONS_WEBHOOK_LIST="; echo "NOTIFICATIONS_PUSHOVER_LIST="; echo "NOTIFICATIONS_PUSHOVER_API="; echo "NOTIFICATIONS_PUSHOVER_USERKEY="; echo "NOTIFICATIONS_INFLUXDB_HOST="; echo "NOTIFICATIONS_INFLUXDB_PORT=8086"; echo "NOTIFICATIONS_INFLUXDB_DB=connmon"; echo "NOTIFICATIONS_INFLUXDB_VERSION=1.8"; echo "NOTIFICATIONS_INFLUXDB_USERNAME="; echo "NOTIFICATIONS_INFLUXDB_PASSWORD="; echo "NOTIFICATIONS_INFLUXDB_APITOKEN="; } > "$SCRIPT_CONF" return 1 fi } PingServer(){ case "$1" in update) while true; do ScriptHeader printf "\\n${BOLD}Current ping destination: %s${CLEARFORMAT}\\n\\n" "$(PingServer check)" printf "1. Enter IP Address\\n" printf "2. Enter Domain\\n" printf "\\ne. Go back\\n" printf "\\n${BOLD}Choose an option:${CLEARFORMAT} " read -r pingoption case "$pingoption" in 1) while true; do printf "\\n${BOLD}Please enter an IP address, or enter e to go back:${CLEARFORMAT} " read -r ipoption if [ "$ipoption" = "e" ]; then break elif Validate_IP "$ipoption"; then sed -i 's/^PINGSERVER=.*$/PINGSERVER='"$ipoption"'/' "$SCRIPT_CONF" break fi done ;; 2) while true; do printf "\\n${BOLD}Please enter a domain name, or enter e to go back:${CLEARFORMAT} " read -r domainoption if [ "$domainoption" = "e" ]; then break elif Validate_Domain "$domainoption"; then sed -i 's/^PINGSERVER=.*$/PINGSERVER='"$domainoption"'/' "$SCRIPT_CONF" break fi done ;; e) printf "\\n" break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done ;; check) PINGSERVER="$(Conf_Parameters check PINGSERVER)" echo "$PINGSERVER" ;; esac } PingDuration(){ case "$1" in update) pingdur=0 exitmenu="" ScriptHeader while true; do printf "\\n${BOLD}Please enter the desired test duration (10-60 seconds):${CLEARFORMAT} " read -r pingdur_choice if [ "$pingdur_choice" = "e" ]; then exitmenu="exit" break elif ! Validate_Number "$pingdur_choice"; then printf "\\n${ERR}Please enter a valid number (10-60)${CLEARFORMAT}\\n" elif [ "$pingdur_choice" -lt 10 ] || [ "$pingdur_choice" -gt 60 ]; then printf "\\n${ERR}Please enter a number between 10 and 60${CLEARFORMAT}\\n" else pingdur="$pingdur_choice" printf "\\n" break fi done if [ "$exitmenu" != "exit" ]; then sed -i 's/^PINGDURATION=.*$/PINGDURATION='"$pingdur"'/' "$SCRIPT_CONF" return 0 else printf "\\n" return 1 fi ;; check) PINGDURATION="$(Conf_Parameters check PINGDURATION)" echo "$PINGDURATION" ;; esac } DaysToKeep(){ case "$1" in update) daystokeep=30 exitmenu="" ScriptHeader while true; do printf "\\n${BOLD}Please enter the desired number of days\\nto keep data for (30-365 days):${CLEARFORMAT} " read -r daystokeep_choice if [ "$daystokeep_choice" = "e" ]; then exitmenu="exit" break elif ! Validate_Number "$daystokeep_choice"; then printf "\\n${ERR}Please enter a valid number (30-365)${CLEARFORMAT}\\n" elif [ "$daystokeep_choice" -lt 30 ] || [ "$daystokeep_choice" -gt 365 ]; then printf "\\n${ERR}Please enter a number between 30 and 365${CLEARFORMAT}\\n" else daystokeep="$daystokeep_choice" printf "\\n" break fi done if [ "$exitmenu" != "exit" ]; then sed -i 's/^DAYSTOKEEP=.*$/DAYSTOKEEP='"$daystokeep"'/' "$SCRIPT_CONF" return 0 else printf "\\n" return 1 fi ;; check) DAYSTOKEEP="$(Conf_Parameters check DAYSTOKEEP)" echo "$DAYSTOKEEP" ;; esac } LastXResults(){ case "$1" in update) lastxresults=10 exitmenu="" ScriptHeader while true; do printf "\\n${BOLD}Please enter the desired number of results\\nto display in the WebUI (1-100):${CLEARFORMAT} " read -r lastx_choice if [ "$lastx_choice" = "e" ]; then exitmenu="exit" break elif ! Validate_Number "$lastx_choice"; then printf "\\n${ERR}Please enter a valid number (1-100)${CLEARFORMAT}\\n" elif [ "$lastx_choice" -lt 1 ] || [ "$lastx_choice" -gt 100 ]; then printf "\\n${ERR}Please enter a number between 1 and 100${CLEARFORMAT}\\n" else lastxresults="$lastx_choice" printf "\\n" break fi done if [ "$exitmenu" != "exit" ]; then sed -i 's/^LASTXRESULTS=.*$/LASTXRESULTS='"$lastxresults"'/' "$SCRIPT_CONF" Generate_LastXResults return 0 else printf "\\n" return 1 fi ;; check) LASTXRESULTS="$(Conf_Parameters check LASTXRESULTS)" echo "$LASTXRESULTS" ;; esac } Auto_ServiceEvent(){ case $1 in create) if [ -f /jffs/scripts/service-event ]; then STARTUPLINECOUNT="$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/service-event)" STARTUPLINECOUNTEX="$(grep -cx 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'"; then { /jffs/scripts/'"$SCRIPT_NAME"' service_event "$@" & }; fi # '"$SCRIPT_NAME" /jffs/scripts/service-event)" if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/service-event fi if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then echo 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'"; then { /jffs/scripts/'"$SCRIPT_NAME"' service_event "$@" & }; fi # '"$SCRIPT_NAME" >> /jffs/scripts/service-event fi else echo "#!/bin/sh" > /jffs/scripts/service-event echo "" >> /jffs/scripts/service-event echo 'if echo "$2" | /bin/grep -q "'"$SCRIPT_NAME"'"; then { /jffs/scripts/'"$SCRIPT_NAME"' service_event "$@" & }; fi # '"$SCRIPT_NAME" >> /jffs/scripts/service-event chmod 0755 /jffs/scripts/service-event fi ;; delete) if [ -f /jffs/scripts/service-event ]; then STARTUPLINECOUNT="$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/service-event)" if [ "$STARTUPLINECOUNT" -gt 0 ]; then sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/service-event fi fi ;; esac } Auto_Startup(){ case $1 in create) if [ -f /jffs/scripts/services-start ]; then STARTUPLINECOUNT="$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/services-start)" if [ "$STARTUPLINECOUNT" -gt 0 ]; then sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/services-start fi fi if [ -f /jffs/scripts/post-mount ]; then STARTUPLINECOUNT="$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/post-mount)" STARTUPLINECOUNTEX="$(grep -cx "/jffs/scripts/$SCRIPT_NAME startup"' "$@" & # '"$SCRIPT_NAME" /jffs/scripts/post-mount)" if [ "$STARTUPLINECOUNT" -gt 1 ] || { [ "$STARTUPLINECOUNTEX" -eq 0 ] && [ "$STARTUPLINECOUNT" -gt 0 ]; }; then sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/post-mount fi if [ "$STARTUPLINECOUNTEX" -eq 0 ]; then echo "/jffs/scripts/$SCRIPT_NAME startup"' "$@" & # '"$SCRIPT_NAME" >> /jffs/scripts/post-mount fi else echo "#!/bin/sh" > /jffs/scripts/post-mount echo "" >> /jffs/scripts/post-mount echo "/jffs/scripts/$SCRIPT_NAME startup"' "$@" & # '"$SCRIPT_NAME" >> /jffs/scripts/post-mount chmod 0755 /jffs/scripts/post-mount fi ;; delete) if [ -f /jffs/scripts/services-start ]; then STARTUPLINECOUNT="$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/services-start)" if [ "$STARTUPLINECOUNT" -gt 0 ]; then sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/services-start fi fi if [ -f /jffs/scripts/post-mount ]; then STARTUPLINECOUNT="$(grep -c '# '"$SCRIPT_NAME" /jffs/scripts/post-mount)" if [ "$STARTUPLINECOUNT" -gt 0 ]; then sed -i -e '/# '"$SCRIPT_NAME"'/d' /jffs/scripts/post-mount fi fi ;; esac } Auto_Cron(){ case $1 in create) STARTUPLINECOUNT="$(cru l | grep -c "$SCRIPT_NAME")" if [ "$STARTUPLINECOUNT" -eq 0 ]; then CRU_DAYNUMBERS="$(Conf_Parameters check SCHDAYS | sed 's/Sun/0/;s/Mon/1/;s/Tues/2/;s/Wed/3/;s/Thurs/4/;s/Fri/5/;s/Sat/6/;')" CRU_HOURS="$(Conf_Parameters check SCHHOURS)" CRU_MINUTES="$(Conf_Parameters check SCHMINS)" cru a "$SCRIPT_NAME" "$CRU_MINUTES $CRU_HOURS * * $CRU_DAYNUMBERS /jffs/scripts/$SCRIPT_NAME generate" echo "$CRU_MINUTES $CRU_HOURS * * $CRU_DAYNUMBERS" > "$SCRIPT_STORAGE_DIR/.cron" fi ;; delete) STARTUPLINECOUNT="$(cru l | grep -c "$SCRIPT_NAME")" if [ "$STARTUPLINECOUNT" -gt 0 ]; then cru d "$SCRIPT_NAME" fi ;; esac } Download_File(){ /usr/sbin/curl -fsL --retry 3 --connect-timeout 15 "$1" -o "$2" } Get_WebUI_Page(){ MyPage="none" for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20; do page="/www/user/user$i.asp" if [ -f "$page" ] && [ "$(md5sum < "$1")" = "$(md5sum < "$page")" ]; then MyPage="user$i.asp" return elif [ "$MyPage" = "none" ] && [ ! -f "$page" ]; then MyPage="user$i.asp" fi done } ### function based on @dave14305's FlexQoS webconfigpage function ### Get_WebUI_URL(){ urlpage="" urlproto="" urldomain="" urlport="" urlpage="$(sed -nE "/$SCRIPT_NAME/ s/.*url\: \"(user[0-9]+\.asp)\".*/\1/p" /tmp/menuTree.js)" if [ "$(nvram get http_enable)" -eq 1 ]; then urlproto="https" else urlproto="http" fi if [ -n "$(nvram get lan_domain)" ]; then urldomain="$(nvram get lan_hostname).$(nvram get lan_domain)" else urldomain="$(nvram get lan_ipaddr)" fi if [ "$(nvram get ${urlproto}_lanport)" -eq 80 ] || [ "$(nvram get ${urlproto}_lanport)" -eq 443 ]; then urlport="" else urlport=":$(nvram get ${urlproto}_lanport)" fi if echo "$urlpage" | grep -qE "user[0-9]+\.asp"; then echo "${urlproto}://${urldomain}${urlport}/${urlpage}" | tr "A-Z" "a-z" else echo "WebUI page not found" fi } ### ### ### locking mechanism code credit to Martineau (@MartineauUK) ### Mount_WebUI(){ Print_Output true "Mounting WebUI tab for $SCRIPT_NAME" "$PASS" LOCKFILE=/tmp/addonwebui.lock FD=386 eval exec "$FD>$LOCKFILE" flock -x "$FD" Get_WebUI_Page "$SCRIPT_DIR/connmonstats_www.asp" if [ "$MyPage" = "none" ]; then Print_Output true "Unable to mount $SCRIPT_NAME WebUI page, exiting" "$CRIT" flock -u "$FD" return 1 fi cp -f "$SCRIPT_DIR/connmonstats_www.asp" "$SCRIPT_WEBPAGE_DIR/$MyPage" echo "$SCRIPT_NAME" > "$SCRIPT_WEBPAGE_DIR/$(echo $MyPage | cut -f1 -d'.').title" if [ "$(/bin/uname -o)" = "ASUSWRT-Merlin" ]; then if [ ! -f /tmp/index_style.css ]; then cp -f /www/index_style.css /tmp/ fi if ! grep -q '.menu_Addons' /tmp/index_style.css ; then echo ".menu_Addons { background: url(ext/shared-jy/addons.png); }" >> /tmp/index_style.css fi umount /www/index_style.css 2>/dev/null mount -o bind /tmp/index_style.css /www/index_style.css if [ ! -f /tmp/menuTree.js ]; then cp -f /www/require/modules/menuTree.js /tmp/ fi sed -i "\\~$MyPage~d" /tmp/menuTree.js if ! grep -q 'menuName: "Addons"' /tmp/menuTree.js ; then lineinsbefore="$(( $(grep -n "exclude:" /tmp/menuTree.js | cut -f1 -d':') - 1))" sed -i "$lineinsbefore"'i,\n{\nmenuName: "Addons",\nindex: "menu_Addons",\ntab: [\n{url: "javascript:var helpwindow=window.open('"'"'/ext/shared-jy/redirect.htm'"'"')", tabName: "Help & Support"},\n{url: "NULL", tabName: "__INHERIT__"}\n]\n}' /tmp/menuTree.js fi sed -i "/url: \"javascript:var helpwindow=window.open('\/ext\/shared-jy\/redirect.htm'/i {url: \"$MyPage\", tabName: \"$SCRIPT_NAME\"}," /tmp/menuTree.js umount /www/require/modules/menuTree.js 2>/dev/null mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js if [ ! -f /tmp/start_apply.htm ]; then cp -f /www/start_apply.htm /tmp/ fi if ! grep -q 'addon_settings' /tmp/start_apply.htm ; then sed -i "/}else if(action_script == \"start_sig_check\"){/i }else if(action_script.indexOf(\"addon_settings\") != -1){ \/\/ do nothing" /tmp/start_apply.htm fi umount /www/start_apply.htm 2>/dev/null mount -o bind /tmp/start_apply.htm /www/start_apply.htm fi flock -u "$FD" Print_Output true "Mounted $SCRIPT_NAME WebUI page as $MyPage" "$PASS" } ExcludeFromQoS(){ case "$1" in enable) sed -i 's/^EXCLUDEFROMQOS=.*$/EXCLUDEFROMQOS=true/' "$SCRIPT_CONF" ;; disable) sed -i 's/^EXCLUDEFROMQOS=.*$/EXCLUDEFROMQOS=false/' "$SCRIPT_CONF" ;; check) EXCLUDEFROMQOS="$(Conf_Parameters check EXCLUDEFROMQOS)" echo "$EXCLUDEFROMQOS" ;; esac } AutomaticMode(){ case "$1" in enable) sed -i 's/^AUTOMATED=.*$/AUTOMATED=true/' "$SCRIPT_CONF" Auto_Cron create 2>/dev/null ;; disable) sed -i 's/^AUTOMATED=.*$/AUTOMATED=false/' "$SCRIPT_CONF" Auto_Cron delete 2>/dev/null ;; check) AUTOMATED="$(Conf_Parameters check AUTOMATED)" if [ "$AUTOMATED" = "true" ]; then return 0; else return 1; fi ;; esac } TestSchedule(){ case "$1" in update) sed -i 's/^SCHDAYS=.*$/SCHDAYS='"$(echo "$2" | sed 's/0/Sun/;s/1/Mon/;s/2/Tues/;s/3/Wed/;s/4/Thurs/;s/5/Fri/;s/6/Sat/;')"'/' "$SCRIPT_CONF" sed -i 's~^SCHHOURS=.*$~SCHHOURS='"$3"'~' "$SCRIPT_CONF" sed -i 's~^SCHMINS=.*$~SCHMINS='"$4"'~' "$SCRIPT_CONF" Auto_Cron delete 2>/dev/null Auto_Cron create 2>/dev/null ;; check) SCHDAYS="$(Conf_Parameters check SCHDAYS)" SCHHOURS="$(Conf_Parameters check SCHHOURS)" SCHMINS="$(Conf_Parameters check SCHMINS)" echo "$SCHDAYS|$SCHHOURS|$SCHMINS" ;; esac } ScriptStorageLocation(){ case "$1" in usb) sed -i 's/^STORAGELOCATION=.*$/STORAGELOCATION=usb/' "$SCRIPT_CONF" mkdir -p "/opt/share/$SCRIPT_NAME.d/" mv "/jffs/addons/$SCRIPT_NAME.d/csv" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/config" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/config.bak" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/connstatstext.js" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/lastx.csv" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/connstats.db" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/.indexcreated" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/.newcolumns" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/.cron" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/.customactioninfo" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/.customactionlist" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null mv "/jffs/addons/$SCRIPT_NAME.d/.emailinfo" "/opt/share/$SCRIPT_NAME.d/" 2>/dev/null SCRIPT_CONF="/opt/share/$SCRIPT_NAME.d/config" ScriptStorageLocation load ;; jffs) sed -i 's/^STORAGELOCATION=.*$/STORAGELOCATION=jffs/' "$SCRIPT_CONF" mkdir -p "/jffs/addons/$SCRIPT_NAME.d/" mv "/opt/share/$SCRIPT_NAME.d/csv" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/config" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/config.bak" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/connstatstext.js" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/lastx.csv" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/connstats.db" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/.indexcreated" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/.newcolumns" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/.cron" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/.customactioninfo" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/.customactionlist" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null mv "/opt/share/$SCRIPT_NAME.d/.emailinfo" "/jffs/addons/$SCRIPT_NAME.d/" 2>/dev/null SCRIPT_CONF="/jffs/addons/$SCRIPT_NAME.d/config" ScriptStorageLocation load ;; check) STORAGELOCATION="$(Conf_Parameters check STORAGELOCATION)" echo "$STORAGELOCATION" ;; load) STORAGELOCATION="$(Conf_Parameters check STORAGELOCATION)" if [ "$STORAGELOCATION" = "usb" ]; then SCRIPT_STORAGE_DIR="/opt/share/$SCRIPT_NAME.d" elif [ "$STORAGELOCATION" = "jffs" ]; then SCRIPT_STORAGE_DIR="/jffs/addons/$SCRIPT_NAME.d" fi CSV_OUTPUT_DIR="$SCRIPT_STORAGE_DIR/csv" USER_SCRIPT_DIR="$SCRIPT_STORAGE_DIR/userscripts.d" ;; esac } OutputTimeMode(){ case "$1" in unix) sed -i 's/^OUTPUTTIMEMODE=.*$/OUTPUTTIMEMODE=unix/' "$SCRIPT_CONF" Generate_CSVs ;; non-unix) sed -i 's/^OUTPUTTIMEMODE=.*$/OUTPUTTIMEMODE=non-unix/' "$SCRIPT_CONF" Generate_CSVs ;; check) OUTPUTTIMEMODE="$(Conf_Parameters check OUTPUTTIMEMODE)" echo "$OUTPUTTIMEMODE" ;; esac } WriteStats_ToJS(){ echo "function $3(){" > "$2" html='document.getElementById("'"$4"'").innerHTML="' while IFS='' read -r line || [ -n "$line" ]; do html="${html}${line}\\r\\n" done < "$1" html="$html"'"' printf "%s\\r\\n}\\r\\n" "$html" >> "$2" } #$1 fieldname $2 tablename $3 frequency (hours) $4 length (days) $5 outputfile $6 outputfrequency $7 sqlfile $8 timestamp WriteSql_ToFile(){ timenow="$8" maxcount="$(echo "$3" "$4" | awk '{printf ((24*$2)/$1)}')" if ! echo "$5" | grep -q "day"; then { echo ".mode csv" echo ".headers on" echo ".output ${5}_${6}.htm" echo "SELECT '$1' Metric,Min(strftime('%s',datetime(strftime('%Y-%m-%d %H:00:00',datetime([Timestamp],'unixepoch'))))) Time,IFNULL(Avg([$1]),'NaN') Value FROM $2 WHERE ([Timestamp] >= strftime('%s',datetime($timenow,'unixepoch','-$maxcount hour'))) GROUP BY strftime('%m',datetime([Timestamp],'unixepoch')),strftime('%d',datetime([Timestamp],'unixepoch')),strftime('%H',datetime([Timestamp],'unixepoch')) ORDER BY [Timestamp] DESC;" } > "$7" else { echo ".mode csv" echo ".headers on" echo ".output ${5}_${6}.htm" echo "SELECT '$1' Metric,Max(strftime('%s',datetime([Timestamp],'unixepoch','start of day'))) Time,IFNULL(Avg([$1]),'NaN') Value FROM $2 WHERE ([Timestamp] > strftime('%s',datetime($timenow,'unixepoch','start of day','+1 day','-$maxcount day'))) GROUP BY strftime('%m',datetime([Timestamp],'unixepoch')),strftime('%d',datetime([Timestamp],'unixepoch')) ORDER BY [Timestamp] DESC;" } > "$7" fi } Run_PingTest(){ if [ ! -f /opt/bin/xargs ]; then Print_Output true "Installing findutils from Entware" opkg update opkg install findutils fi if [ -n "$PPID" ]; then ps | grep -v grep | grep -v $$ | grep -v "$PPID" | grep -i "$SCRIPT_NAME" | grep generate | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 else ps | grep -v grep | grep -v $$ | grep -i "$SCRIPT_NAME" | grep generate | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 fi Create_Dirs Conf_Exists Auto_Startup create 2>/dev/null if AutomaticMode check; then Auto_Cron create 2>/dev/null; else Auto_Cron delete 2>/dev/null; fi Auto_ServiceEvent create 2>/dev/null ScriptStorageLocation load Create_Symlinks pingfile=/tmp/pingfile.txt resultfile="$SCRIPT_WEB_DIR/ping-result.txt" pingduration="$(PingDuration check)" pingtarget="$(PingServer check)" pingtargetip="" completepingtarget="" rm -f "$resultfile" rm -f "$pingfile" echo 'var connmonstatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_connmon.js" Print_Output false "$pingduration second ping test to $pingtarget starting..." "$PASS" if ! expr "$pingtarget" : '[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$' >/dev/null && nslookup "$pingtarget" >/dev/null 2>&1; then pingtargetip="$(dig +short +answer "$pingtarget" | head -n 1)" completepingtarget="$pingtarget ($pingtargetip)" else pingtargetip="$pingtarget" completepingtarget="$pingtarget" fi stoppedqos="false" if [ "$(ExcludeFromQoS check)" = "true" ]; then if [ "$(nvram get qos_enable)" -eq 1 ] && [ "$(nvram get qos_type)" -eq 1 ]; then for ACTION in -D -A ; do iptables "$ACTION" OUTPUT -p icmp -j MARK --set-xmark 0x80000000/0xC0000000 2>/dev/null iptables -t mangle "$ACTION" OUTPUT -p icmp -j MARK --set-xmark 0x80000000/0xC0000000 2>/dev/null iptables -t mangle "$ACTION" POSTROUTING -p icmp -j MARK --set-xmark 0x80000000/0xC0000000 2>/dev/null stoppedqos="true" done elif [ "$(nvram get qos_enable)" -eq 1 ] && [ "$(nvram get qos_type)" -ne 1 ] && [ -f /tmp/qos ]; then /tmp/qos stop >/dev/null 2>&1 stoppedqos="true" elif [ "$(nvram get qos_enable)" -eq 0 ] && [ -f /jffs/addons/cake-qos/cake-qos ]; then /jffs/addons/cake-qos/cake-qos stop >/dev/null 2>&1 stoppedqos="true" fi fi ping -w "$pingduration" "$pingtargetip" > "$pingfile" if [ "$stoppedqos" = "true" ]; then if [ "$(nvram get qos_enable)" -eq 1 ] && [ "$(nvram get qos_type)" -eq 1 ]; then iptables -D OUTPUT -p icmp -j MARK --set-xmark 0x80000000/0xC0000000 2>/dev/null iptables -t mangle -D OUTPUT -p icmp -j MARK --set-xmark 0x80000000/0xC0000000 2>/dev/null iptables -t mangle -D POSTROUTING -p icmp -j MARK --set-xmark 0x80000000/0xC0000000 2>/dev/null elif [ "$(nvram get qos_enable)" -eq 1 ] && [ "$(nvram get qos_type)" -ne 1 ] && [ -f /tmp/qos ]; then /tmp/qos start >/dev/null 2>&1 elif [ "$(nvram get qos_enable)" -eq 0 ] && [ -f /jffs/addons/cake-qos/cake-qos ]; then /jffs/addons/cake-qos/cake-qos start >/dev/null 2>&1 fi fi ScriptStorageLocation load PREVPING=0 TOTALDIFF=0 COUNTER=1 PINGLIST="$(grep seq= "$pingfile")" PINGCOUNT="$(echo "$PINGLIST" | sed '/^\s*$/d' | wc -l)" DIFFCOUNT="$((PINGCOUNT - 1))" if [ "$PINGCOUNT" -gt 0 ]; then until [ "$COUNTER" -gt "$PINGCOUNT" ]; do CURPING="$(echo "$PINGLIST" | sed -n "$COUNTER"p | cut -f4 -d"=" | cut -f1 -d" ")" if [ "$COUNTER" -gt 1 ]; then DIFF="$(echo "$CURPING" "$PREVPING" | awk '{printf "%4.3f\n",$1-$2}')" NEG="$(echo "$DIFF" 0 | awk '{ if ($1 < $2) print "neg"; else print "pos"}')" if [ "$NEG" = "neg" ]; then DIFF="$(echo "$DIFF" "-1" | awk '{printf "%4.3f\n",$1*$2}')"; fi TOTALDIFF="$(echo "$TOTALDIFF" "$DIFF" | awk '{printf "%4.3f\n",$1+$2}')" fi PREVPING="$CURPING" COUNTER="$((COUNTER + 1))" done fi TZ="$(cat /etc/TZ)" export TZ timenow="$(/bin/date +"%s")" timenowfriendly="$(/bin/date +"%c")" ping=0 jitter=0 linequal=0 if [ "$PINGCOUNT" -gt 1 ]; then ping="$(tail -n 1 "$pingfile" | cut -f4 -d"/")" jitter="$(echo "$TOTALDIFF" "$DIFFCOUNT" | awk '{printf "%4.3f\n",$1/$2}')" pkt_trans="$(tail -n 2 "$pingfile" | head -n 1 | cut -f1 -d"," | cut -f1 -d" ")" pkt_rec="$(tail -n 2 "$pingfile" | head -n 1 | cut -f2 -d"," | cut -f2 -d" ")" linequal="$(echo "$pkt_rec" "$pkt_trans" | awk '{printf "%4.3f\n",100*$1/$2}')" fi Process_Upgrade { echo "CREATE TABLE IF NOT EXISTS [connstats] ([StatID] INTEGER PRIMARY KEY NOT NULL,[Timestamp] NUMERIC NOT NULL,[Ping] REAL NOT NULL,[Jitter] REAL NOT NULL,[LineQuality] REAL NOT NULL,[PingTarget] TEXT NOT NULL,[PingDuration] NUMERIC);" echo "INSERT INTO connstats ([Timestamp],[Ping],[Jitter],[LineQuality],[PingTarget],[PingDuration]) values($timenow,$ping,$jitter,$linequal,'$completepingtarget',$pingduration);" } > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql { echo "DELETE FROM [connstats] WHERE [Timestamp] < strftime('%s',datetime($timenow,'unixepoch','-$(DaysToKeep check) day'));" echo "PRAGMA analysis_limit=0;" echo "PRAGMA cache_size=-20000;" echo "ANALYZE connstats;" } > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql >/dev/null 2>&1 rm -f /tmp/connmon-stats.sql echo 'var connmonstatus = "GenerateCSV";' > "$SCRIPT_WEB_DIR/detect_connmon.js" Generate_CSVs echo "Stats last updated: $timenowfriendly" > /tmp/connstatstitle.txt WriteStats_ToJS /tmp/connstatstitle.txt "$SCRIPT_STORAGE_DIR/connstatstext.js" setConnmonStatsTitle statstitle Print_Output false "Test results - Ping $ping ms - Jitter - $jitter ms - Line Quality $linequal %" "$PASS" { printf "Ping test result\\n" printf "\\nPing %s ms - Jitter - %s ms - Line Quality %s %%\\n" "$ping" "$jitter" "$linequal" } >> "$resultfile" rm -f "$pingfile" rm -f /tmp/connstatstitle.txt TriggerNotifications PingTest "$timenowfriendly" "$ping ms" "$jitter ms" "$linequal %" "$timenow" if [ "$(echo "$ping" "$(Conf_Parameters check NOTIFICATIONS_PINGTHRESHOLD_VALUE)" | awk '{print ($1 > $2)}')" -eq 1 ]; then TriggerNotifications PingThreshold "$timenowfriendly" "$ping ms" "$(Conf_Parameters check NOTIFICATIONS_PINGTHRESHOLD_VALUE) ms" fi if [ "$(echo "$jitter" "$(Conf_Parameters check NOTIFICATIONS_JITTERTHRESHOLD_VALUE)" | awk '{print ($1 > $2)}')" -eq 1 ]; then TriggerNotifications JitterThreshold "$timenowfriendly" "$jitter ms" "$(Conf_Parameters check NOTIFICATIONS_JITTERTHRESHOLD_VALUE) ms" fi if [ "$(echo "$linequal" "$(Conf_Parameters check NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE)" | awk '{print ($1 < $2)}')" -eq 1 ]; then TriggerNotifications LineQualityThreshold "$timenowfriendly" "$linequal %" "$(Conf_Parameters check NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE) %" fi echo 'var connmonstatus = "Done";' > "$SCRIPT_WEB_DIR/detect_connmon.js" } Generate_CSVs(){ Process_Upgrade renice 15 $$ OUTPUTTIMEMODE="$(OutputTimeMode check)" TZ="$(cat /etc/TZ)" export TZ timenow="$(/bin/date +"%s")" timenowfriendly="$(/bin/date +"%c")" metriclist="Ping Jitter LineQuality" for metric in $metriclist; do { echo ".mode csv" echo ".headers on" echo ".output $CSV_OUTPUT_DIR/${metric}_raw_daily.htm" echo "SELECT '$metric' Metric,[Timestamp] Time,[$metric] Value FROM connstats WHERE ([Timestamp] >= strftime('%s',datetime($timenow,'unixepoch','-1 day'))) ORDER BY [Timestamp] DESC;" } > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql { echo ".mode csv" echo ".headers on" echo ".output $CSV_OUTPUT_DIR/${metric}_raw_weekly.htm" echo "SELECT '$metric' Metric,[Timestamp] Time,[$metric] Value FROM connstats WHERE ([Timestamp] >= strftime('%s',datetime($timenow,'unixepoch','-7 day'))) ORDER BY [Timestamp] DESC;" } > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql { echo ".mode csv" echo ".headers on" echo ".output $CSV_OUTPUT_DIR/${metric}_raw_monthly.htm" echo "SELECT '$metric' Metric,[Timestamp] Time,[$metric] Value FROM connstats WHERE ([Timestamp] >= strftime('%s',datetime($timenow,'unixepoch','-30 day'))) ORDER BY [Timestamp] DESC;" } > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql WriteSql_ToFile "$metric" connstats 1 1 "$CSV_OUTPUT_DIR/${metric}_hour" daily /tmp/connmon-stats.sql "$timenow" "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql WriteSql_ToFile "$metric" connstats 1 7 "$CSV_OUTPUT_DIR/${metric}_hour" weekly /tmp/connmon-stats.sql "$timenow" "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql WriteSql_ToFile "$metric" connstats 1 30 "$CSV_OUTPUT_DIR/${metric}_hour" monthly /tmp/connmon-stats.sql "$timenow" "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql WriteSql_ToFile "$metric" connstats 24 1 "$CSV_OUTPUT_DIR/${metric}_day" daily /tmp/connmon-stats.sql "$timenow" "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql WriteSql_ToFile "$metric" connstats 24 7 "$CSV_OUTPUT_DIR/${metric}_day" weekly /tmp/connmon-stats.sql "$timenow" "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql WriteSql_ToFile "$metric" connstats 24 30 "$CSV_OUTPUT_DIR/${metric}_day" monthly /tmp/connmon-stats.sql "$timenow" "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql rm -f "$CSV_OUTPUT_DIR/${metric}daily.htm" rm -f "$CSV_OUTPUT_DIR/${metric}weekly.htm" rm -f "$CSV_OUTPUT_DIR/${metric}monthly.htm" done rm -f /tmp/connmon-stats.sql Generate_LastXResults { echo ".mode csv" echo ".headers on" echo ".output $CSV_OUTPUT_DIR/CompleteResults.htm" } > /tmp/connmon-complete.sql echo "SELECT [Timestamp],[Ping],[Jitter],[LineQuality],[PingTarget],[PingDuration] FROM connstats WHERE ([Timestamp] >= strftime('%s',datetime($timenow,'unixepoch','-$(DaysToKeep check) day'))) ORDER BY [Timestamp] DESC;" >> /tmp/connmon-complete.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-complete.sql rm -f /tmp/connmon-complete.sql sed -i 's/"//g' "$CSV_OUTPUT_DIR/CompleteResults.htm" dos2unix "$CSV_OUTPUT_DIR/"*.htm tmpoutputdir="/tmp/${SCRIPT_NAME}results" mkdir -p "$tmpoutputdir" mv "$CSV_OUTPUT_DIR/CompleteResults.htm" "$tmpoutputdir/CompleteResults.htm" if [ "$OUTPUTTIMEMODE" = "unix" ]; then find "$tmpoutputdir/" -name '*.htm' -exec sh -c 'i="$1"; mv -- "$i" "${i%.htm}.csv"' _ {} \; elif [ "$OUTPUTTIMEMODE" = "non-unix" ]; then for i in "$tmpoutputdir/"*".htm"; do awk -F"," 'NR==1 {OFS=","; print} NR>1 {OFS=","; $1=strftime("%Y-%m-%d %H:%M:%S", $1); print }' "$i" > "$i.out" done find "$tmpoutputdir/" -name '*.htm.out' -exec sh -c 'i="$1"; mv -- "$i" "${i%.htm.out}.csv"' _ {} \; rm -f "$tmpoutputdir/"*.htm fi mv "$tmpoutputdir/CompleteResults.csv" "$CSV_OUTPUT_DIR/CompleteResults.htm" rm -f "$CSV_OUTPUT_DIR/connmondata.zip" rm -rf "$tmpoutputdir" renice 0 $$ } Generate_LastXResults(){ rm -f "$SCRIPT_STORAGE_DIR/connjs.js" rm -f "$SCRIPT_STORAGE_DIR/lastx.htm" { echo ".mode csv" echo ".output /tmp/conn-lastx.csv" echo "SELECT [Timestamp],[Ping],[Jitter],[LineQuality],[PingTarget],[PingDuration] FROM connstats ORDER BY [Timestamp] DESC LIMIT $(LastXResults check);" } > /tmp/conn-lastx.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/conn-lastx.sql rm -f /tmp/conn-lastx.sql sed -i 's/"//g' /tmp/conn-lastx.csv mv /tmp/conn-lastx.csv "$SCRIPT_STORAGE_DIR/lastx.csv" } Reset_DB(){ SIZEAVAIL="$(df -P -k "$SCRIPT_STORAGE_DIR" | awk '{print $4}' | tail -n 1)" SIZEDB="$(ls -l "$SCRIPT_STORAGE_DIR/connstats.db" | awk '{print $5}')" SIZEAVAIL="$(echo "$SIZEAVAIL" | awk '{printf("%s", $1*1024);}')" if [ "$(echo "$SIZEAVAIL $SIZEDB" | awk '{print ($1 < $2)}')" -eq 1 ]; then Print_Output true "Database size exceeds available space. $(ls -lh "$SCRIPT_STORAGE_DIR/connstats.db" | awk '{print $5}')B is required to create backup." "$ERR" return 1 else Print_Output true "Sufficient free space to back up database, proceeding..." "$PASS" if ! cp -a "$SCRIPT_STORAGE_DIR/connstats.db" "$SCRIPT_STORAGE_DIR/connstats.db.bak"; then Print_Output true "Database backup failed, please check storage device" "$WARN" fi echo "DELETE FROM [connstats];" > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql rm -f /tmp/connmon-stats.sql Print_Output true "Database reset complete" "$WARN" fi } Process_Upgrade(){ rm -f "$SCRIPT_STORAGE_DIR/.tableupgraded" if [ ! -f "$SCRIPT_STORAGE_DIR/.indexcreated" ]; then renice 15 $$ Print_Output true "Creating database table indexes..." "$PASS" echo "CREATE INDEX IF NOT EXISTS idx_time_ping ON connstats (Timestamp,Ping);" > /tmp/connmon-upgrade.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-upgrade.sql echo "CREATE INDEX IF NOT EXISTS idx_time_jitter ON connstats (Timestamp,Jitter);" > /tmp/connmon-upgrade.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-upgrade.sql echo "CREATE INDEX IF NOT EXISTS idx_time_linequality ON connstats (Timestamp,LineQuality);" > /tmp/connmon-upgrade.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-upgrade.sql rm -f /tmp/connmon-upgrade.sql touch "$SCRIPT_STORAGE_DIR/.indexcreated" Print_Output true "Database ready, continuing..." "$PASS" renice 0 $$ fi if [ ! -f "$SCRIPT_STORAGE_DIR/.newcolumns" ]; then echo "ALTER TABLE connstats ADD COLUMN PingTarget [TEXT] NOT NULL DEFAULT '';" > /tmp/connmon-upgrade.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-upgrade.sql echo "ALTER TABLE connstats ADD COLUMN PingDuration [NUMERIC];" > /tmp/connmon-upgrade.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-upgrade.sql rm -f /tmp/connmon-upgrade.sql touch "$SCRIPT_STORAGE_DIR/.newcolumns" fi if [ ! -f "$SCRIPT_STORAGE_DIR/lastx.csv" ]; then Generate_LastXResults fi if [ ! -f /opt/bin/dig ]; then opkg update opkg install bind-dig fi if [ ! -f "$SCRIPT_STORAGE_DIR/.cron" ]; then cru l | grep "$SCRIPT_NAME" | cut -f1-5 -d' ' > "$SCRIPT_STORAGE_DIR/.cron" fi if [ ! -f "$SCRIPT_STORAGE_DIR/.customactioninfo" ]; then CustomAction_Info silent fi if [ ! -f "$SCRIPT_STORAGE_DIR/.customactionlist" ]; then CustomAction_List silent fi if [ ! -f "$SCRIPT_STORAGE_DIR/.emailinfo" ]; then Email_Header silent fi if [ ! -f /tmp/start_apply.htm ]; then cp -f /www/start_apply.htm /tmp/ if ! grep -q 'addon_settings' /tmp/start_apply.htm ; then sed -i "/}else if(action_script == \"start_sig_check\"){/i }else if(action_script.indexOf(\"addon_settings\") != -1){ \/\/ do nothing" /tmp/start_apply.htm fi umount /www/start_apply.htm 2>/dev/null mount -o bind /tmp/start_apply.htm /www/start_apply.htm fi if [ ! -f "$SCRIPT_DIR/CHANGELOG.md" ]; then Update_File CHANGELOG.md fi if [ ! -f "$SCRIPT_STORAGE_DIR/connstatstext.js" ]; then echo "Stats last updated: Not yet updated" > /tmp/connstatstitle.txt WriteStats_ToJS /tmp/connstatstitle.txt "$SCRIPT_STORAGE_DIR/connstatstext.js" setConnmonStatsTitle statstitle fi } Shortcut_Script(){ case $1 in create) if [ -d /opt/bin ] && [ ! -f "/opt/bin/$SCRIPT_NAME" ] && [ -f "/jffs/scripts/$SCRIPT_NAME" ]; then ln -s "/jffs/scripts/$SCRIPT_NAME" /opt/bin chmod 0755 "/opt/bin/$SCRIPT_NAME" fi ;; delete) if [ -f "/opt/bin/$SCRIPT_NAME" ]; then rm -f "/opt/bin/$SCRIPT_NAME" fi ;; esac } PressEnter(){ while true; do printf "Press enter to continue..." read -r key case "$key" in *) break ;; esac done return 0 } Email_ConfExists(){ if [ -f "$EMAIL_CONF" ]; then dos2unix "$EMAIL_CONF" chmod 0644 "$EMAIL_CONF" . "$EMAIL_CONF" return 0 else { echo "# Email settings (mail envelope) #" echo "FROM_ADDRESS=\"\"" echo "TO_NAME=\"\"" echo "TO_ADDRESS=\"\"" echo "FRIENDLY_ROUTER_NAME=\"\"" echo "" echo "# Email credentials #" echo "USERNAME=\"\"" echo "# Encrypted Password is stored in emailpw.enc file." echo "emailPwEnc=\"\"" echo "" echo "# Server settings #" echo "SMTP=\"\"" echo "PORT=\"\"" echo "PROTOCOL=\"\"" echo "SSL_FLAG=\"\"" } > "$EMAIL_CONF" return 1 fi } Email_Header(){ if [ -z "$1" ]; then printf "If you have Two Factor Authentication (2FA) enabled you need to\\n" printf "use an App password.\\n\\n" printf "${BOLD}Common SMTP Server settings${CLEARFORMAT}\\n" printf "%s\\n" "------------------------------------------------" printf "Provider Server Port Protocol\\n" printf "%s\\n" "------------------------------------------------" printf "Gmail smtp.gmail.com 465 smtps\\n" printf "mail.com smtp.mail.com 587 smtp\\n" printf "Yahoo! smtp.mail.yahoo.com 465 smtps\\n" printf "outlook.com smtp-mail.outlook.com 587 smtp\\n" printf "%s\\n\\n" "------------------------------------------------" fi { printf "If you have Two Factor Authentication (2FA) enabled you need to use an App password.\\n\\n" printf "Common SMTP Server settings\\n" printf "%s\\n" "------------------------------------------------" printf "Provider Server Port Protocol\\n" printf "%s\\n" "------------------------------------------------" printf "Gmail smtp.gmail.com 465 smtps\\n" printf "mail.com smtp.mail.com 587 smtp\\n" printf "Yahoo! smtp.mail.yahoo.com 465 smtps\\n" printf "outlook.com smtp-mail.outlook.com 587 smtp\\n" printf "%s" "------------------------------------------------" } > "$SCRIPT_STORAGE_DIR/.emailinfo" } Email_EmailAddress(){ EMAIL_ADDRESS="" while true; do printf "\\n${BOLD}Enter email address:${CLEARFORMAT} " read -r EMAIL_ADDRESS if [ "$EMAIL_ADDRESS" = "e" ]; then EMAIL_ADDRESS="" break elif ! echo "$EMAIL_ADDRESS" | grep -qE "$EMAIL_REGEX"; then printf "\\n${ERR}Please enter a valid email address${CLEARFORMAT}\\n" else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) if [ "$1" = "From" ]; then sed -i 's/^FROM_ADDRESS=.*$/FROM_ADDRESS="'"$EMAIL_ADDRESS"'"/' "$EMAIL_CONF" elif [ "$1" = "To" ]; then sed -i 's/^TO_ADDRESS=.*$/TO_ADDRESS="'"$EMAIL_ADDRESS"'"/' "$EMAIL_CONF" elif [ "$1" = "Override" ]; then NOTIFICATIONS_EMAIL_LIST="$(Email_Recipients check),$EMAIL_ADDRESS" NOTIFICATIONS_EMAIL_LIST="$(echo "$NOTIFICATIONS_EMAIL_LIST" | sed 's/,,/,/g;s/,$//;s/^,//')" sed -i 's/^NOTIFICATIONS_EMAIL_LIST=.*$/NOTIFICATIONS_EMAIL_LIST='"$NOTIFICATIONS_EMAIL_LIST"'/' "$SCRIPT_CONF" fi break ;; *) : ;; esac fi done } Email_RouterName(){ FRIENDLY_ROUTER_NAME="" while true; do printf "\\n${BOLD}Enter friendly router name:${CLEARFORMAT} " read -r FRIENDLY_ROUTER_NAME if [ "$FRIENDLY_ROUTER_NAME" = "e" ]; then FRIENDLY_ROUTER_NAME="" break elif [ "$(printf "%s" "$FRIENDLY_ROUTER_NAME" | wc -m)" -lt 2 ] || [ "$(printf "%s" "$FRIENDLY_ROUTER_NAME" | wc -m)" -gt 16 ]; then printf "\\n${ERR}Router friendly name must be between 2 and 16 characters${CLEARFORMAT}\\n" elif echo "$FRIENDLY_ROUTER_NAME" | grep -q "^-" || echo "$FRIENDLY_ROUTER_NAME" | grep -q "^_"; then printf "\\n${ERR}Router friendly name must not start with dash (-) or underscore (_)${CLEARFORMAT}\\n" elif echo "$FRIENDLY_ROUTER_NAME" | grep -q "[-]$" || echo "$FRIENDLY_ROUTER_NAME" | grep -q "_$"; then printf "\\n${ERR}Router friendly name must not end with dash (-) or underscore (_)${CLEARFORMAT}\\n" elif ! echo "$FRIENDLY_ROUTER_NAME" | grep -qE "^[a-zA-Z0-9_\-]*$"; then printf "\\n${ERR}Router friendly name must not contain special characters other than dash (-) or underscore (_)${CLEARFORMAT}\\n" else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) sed -i 's/^FRIENDLY_ROUTER_NAME=.*$/FRIENDLY_ROUTER_NAME="'"$FRIENDLY_ROUTER_NAME"'"/' "$EMAIL_CONF" break ;; *) : ;; esac fi done } Email_Server(){ SMTP="" while true; do printf "\\n${BOLD}Enter SMTP Server:${CLEARFORMAT} " read -r SMTP if [ "$SMTP" = "e" ]; then SMTP="" break elif ! Validate_Domain "$SMTP"; then printf "\\n${ERR}Domain cannot be resolved by nslookup, please ensure you enter a valid domain name${CLEARFORMAT}\\n" else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) sed -i 's/^SMTP=.*$/SMTP="'"$SMTP"'"/' "$EMAIL_CONF" break ;; *) : ;; esac fi done } Email_Protocol(){ while true; do printf "\\n${BOLD}Please choose the protocol for your email provider:${CLEARFORMAT}\\n" printf " 1. smtp\\n" printf " 2. smtps\\n\\n" printf "Choose an option: " read -r protomenu case "$protomenu" in 1) sed -i 's/^PROTOCOL=.*$/PROTOCOL="smtp"/' "$EMAIL_CONF" break ;; 2) sed -i 's/^PROTOCOL=.*$/PROTOCOL="smtps"/' "$EMAIL_CONF" break ;; e) break ;; *) printf "\\n${ERR}Please enter a valid choice (1-2)${CLEARFORMAT}\\n" ;; esac done } Email_SSL(){ SSL_FLAG="" while true; do printf "\\n${BOLD}Please choose the SSL security level:${CLEARFORMAT}\\n" printf " 1. Secure (recommended)\\n" printf " 2. Insecure (choose this if you see SSL errors)\\n\\n" printf "Choose an option: " read -r protomenu case "$protomenu" in 1) sed -i 's/^SSL_FLAG=.*$/SSL_FLAG=""/' "$EMAIL_CONF" break ;; 2) sed -i 's/^SSL_FLAG=.*$/SSL_FLAG="--insecure"/' "$EMAIL_CONF" break ;; e) SSL_FLAG="e" break ;; *) printf "\\n${ERR}Please enter a valid choice (1-2)${CLEARFORMAT}\\n" ;; esac done } Email_Password(){ PASSWORD="" while true; do printf "\\n${BOLD}Enter Password:${CLEARFORMAT} " read -r PASSWORD if [ "$PASSWORD" = "e" ]; then PASSWORD="" break else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) Email_Encrypt_Password "$PASSWORD" PASSWORD="" break ;; *) : ;; esac fi done } Email_Encrypt_Password(){ PWENCFILE="$EMAIL_DIR/emailpw.enc" emailPwEnc="$(grep "emailPwEnc=" "$EMAIL_CONF" | cut -f2 -d"=" | sed 's/""//')" if [ -f /usr/sbin/openssl11 ]; then printf "$1" | /usr/sbin/openssl11 aes-256-cbc $emailPwEnc -out "$PWENCFILE" -pass pass:ditbabot,isoi else printf "$1" | /usr/sbin/openssl aes-256-cbc $emailPwEnc -out "$PWENCFILE" -pass pass:ditbabot,isoi fi } Email_Decrypt_Password(){ PWENCFILE="$EMAIL_DIR/emailpw.enc" if /usr/sbin/openssl aes-256-cbc -d -in "$PWENCFILE" -pass pass:ditbabot,isoi >/dev/null 2>&1 ; then # old OpenSSL 1.0.x PASSWORD="$(/usr/sbin/openssl aes-256-cbc -d -in "$PWENCFILE" -pass pass:ditbabot,isoi 2>/dev/null)" elif /usr/sbin/openssl aes-256-cbc -d -md md5 -in "$PWENCFILE" -pass pass:ditbabot,isoi >/dev/null 2>&1 ; then # new OpenSSL 1.1.x non-converted password PASSWORD="$(/usr/sbin/openssl aes-256-cbc -d -md md5 -in "$PWENCFILE" -pass pass:ditbabot,isoi 2>/dev/null)" elif /usr/sbin/openssl aes-256-cbc $emailPwEnc -d -in "$PWENCFILE" -pass pass:ditbabot,isoi >/dev/null 2>&1 ; then # new OpenSSL 1.1.x converted password with -pbkdf2 flag PASSWORD="$(/usr/sbin/openssl aes-256-cbc $emailPwEnc -d -in "$PWENCFILE" -pass pass:ditbabot,isoi 2>/dev/null)" fi echo "$PASSWORD" } Email_Recipients(){ case "$1" in update) while true; do ScriptHeader printf "${BOLD}${UNDERLINE}Email Recipients Override List${CLEARFORMAT}\\n\\n" NOTIFICATIONS_EMAIL_LIST="$(Email_Recipients check)" if [ "$NOTIFICATIONS_EMAIL_LIST" = "" ]; then NOTIFICATIONS_EMAIL_LIST="Generic To Address will be used" fi printf "Currently: ${SETTING}${NOTIFICATIONS_EMAIL_LIST}${CLEARFORMAT}\\n\\n" printf "Available options:\\n" printf "1. Update list\\n" printf "2. Clear list\\n" printf "e. Go back\\n\\n" printf "Choose an option: " read -r emailrecipientmenu case "$emailrecipientmenu" in 1) Email_EmailAddress Override ;; 2) sed -i 's/^NOTIFICATIONS_EMAIL_LIST=.*$/NOTIFICATIONS_EMAIL_LIST=/' "$SCRIPT_CONF" ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done ;; check) NOTIFICATIONS_EMAIL_LIST="$(Conf_Parameters check NOTIFICATIONS_EMAIL_LIST)" echo "$NOTIFICATIONS_EMAIL_LIST" ;; esac } # encode image for email inline # $1 : image content id filename (match the cid:filename.png in html document) # $2 : image content base64 encoded # $3 : output file Encode_Image(){ { echo ""; echo "--MULTIPART-RELATED-BOUNDARY"; echo "Content-Type: image/png;name=\"$1\""; echo "Content-Transfer-Encoding: base64"; echo "Content-Disposition: inline;filename=\"$1\""; echo "Content-Id: <$1>"; echo ""; echo "$2"; } >> "$3" } # encode text for email inline # $1 : text content base64 encoded # $2 : output file Encode_Text(){ { echo ""; echo "--MULTIPART-RELATED-BOUNDARY"; echo "Content-Type: text/plain;name=\"$1\""; echo "Content-Transfer-Encoding: quoted-printable"; echo "Content-Disposition: attachment;filename=\"$1\""; echo ""; echo "$2"; } >> "$3" } SendEmail(){ if ! Email_ConfExists; then return 1 else EMAILSUBJECT="$1" EMAILCONTENTS="$2" if [ -n "$3" ]; then TO_ADDRESS="$3" fi if [ -z "$TO_ADDRESS" ]; then Print_Output false "No email recipient specified" "$ERR" return 1 fi # html message to send # { echo "From: \"connmon\" <$FROM_ADDRESS>" echo "To: \"$TO_ADDRESS\" <$TO_ADDRESS>" echo "Subject: $EMAILSUBJECT" echo "Date: $(/bin/date -R)" echo "MIME-Version: 1.0" echo "Content-Type: multipart/mixed; boundary=\"MULTIPART-MIXED-BOUNDARY\"" echo "" echo "--MULTIPART-MIXED-BOUNDARY" echo "Content-Type: multipart/related; boundary=\"MULTIPART-RELATED-BOUNDARY\"" echo "" echo "--MULTIPART-RELATED-BOUNDARY" echo "Content-Type: multipart/alternative; boundary=\"MULTIPART-ALTERNATIVE-BOUNDARY\"" } > /tmp/mail.txt #echo "

$2" > /tmp/message.html echo "$EMAILCONTENTS" > /tmp/message.html echo "" >> /tmp/message.html message_base64="$(openssl base64 -A < /tmp/message.html)" rm -f /tmp/message.html { echo "" echo "--MULTIPART-ALTERNATIVE-BOUNDARY" echo "Content-Type: text/html; charset=utf-8" echo "Content-Transfer-Encoding: base64" echo "" echo "$message_base64" echo "" echo "--MULTIPART-ALTERNATIVE-BOUNDARY--" echo "" } >> /tmp/mail.txt #image_base64="$(openssl base64 -A < "connmonlogo.png")" #Encode_Image "connmonlogo.png" "$image_base64" /tmp/mail.txt #Encode_Text vnstat.txt "$(cat "$VNSTAT_OUTPUT_FILE")" /tmp/mail.txt { echo "--MULTIPART-RELATED-BOUNDARY--" echo "" echo "--MULTIPART-MIXED-BOUNDARY--" } >> /tmp/mail.txt PASSWORD="$(Email_Decrypt_Password)" /usr/sbin/curl -s --show-error --url "$PROTOCOL://$SMTP:$PORT" \ --mail-from "$FROM_ADDRESS" --mail-rcpt "$TO_ADDRESS" \ --upload-file /tmp/mail.txt \ --ssl-reqd \ --user "$USERNAME:$PASSWORD" $SSL_FLAG if [ $? -eq 0 ]; then echo "" Print_Output false "Email sent successfully" "$PASS" rm -f /tmp/mail.txt PASSWORD="" return 0 else echo "" Print_Output true "Email failed to send" "$ERR" rm -f /tmp/mail.txt PASSWORD="" return 1 fi fi } Webhook_Targets(){ case "$1" in update) while true; do ScriptHeader printf "${BOLD}${UNDERLINE}Discord Webhook List${CLEARFORMAT}\\n\\n" NOTIFICATIONS_WEBHOOK_LIST="$(Webhook_Targets check | sed 's~,~\n~g')" printf "Currently: ${SETTING}${NOTIFICATIONS_WEBHOOK_LIST}${CLEARFORMAT}\\n\\n" printf "Available options:\\n" printf "1. Update list\\n" printf "2. Clear list\\n" printf "e. Go back\\n\\n" printf "Choose an option: " read -r webhooktargetmenu case "$webhooktargetmenu" in 1) Notification_String "Webhook Target" ;; 2) Conf_Parameters clear "Webhook Target" ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done ;; check) NOTIFICATIONS_WEBHOOK_LIST="$(Conf_Parameters check NOTIFICATIONS_WEBHOOK_LIST)" echo "$NOTIFICATIONS_WEBHOOK_LIST" ;; esac } SendWebhook(){ WEBHOOKCONTENT="$1" WEBHOOKTARGET="$2" if [ -z "$WEBHOOKTARGET" ]; then Print_Output false "No Webhook URL specified" "$ERR" return 1 fi /usr/sbin/curl -fsL --retry 3 --connect-timeout 15 --output /dev/null -H "Content-Type: application/json" \ -d '{"username":"'"$SCRIPT_NAME"'","content":"'"$WEBHOOKCONTENT"'"}' "$WEBHOOKTARGET" if [ $? -eq 0 ]; then echo "" Print_Output false "Webhook sent successfully" "$PASS" return 0 else echo "" Print_Output true "Webhook failed to send" "$ERR" return 1 fi } Pushover_Devices(){ case "$1" in update) while true; do ScriptHeader printf "${BOLD}${UNDERLINE}Pushover Device List${CLEARFORMAT}\\n\\n" NOTIFICATIONS_PUSHOVER_LIST="$(Pushover_Devices check | sed 's~,~\n~g')" printf "Currently: ${SETTING}${NOTIFICATIONS_PUSHOVER_LIST}${CLEARFORMAT}\\n\\n" printf "Available options:\\n" printf "1. Update list\\n" printf "2. Clear list\\n" printf "e. Go back\\n\\n" printf "Choose an option: " read -r pushoverdevicemenu case "$pushoverdevicemenu" in 1) Notification_String "Pushover Device" ;; 2) Conf_Parameters clear "Pushover Device" ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done ;; check) NOTIFICATIONS_PUSHOVER_LIST="$(Conf_Parameters check NOTIFICATIONS_PUSHOVER_LIST)" echo "$NOTIFICATIONS_PUSHOVER_LIST" ;; esac } SendPushover(){ PUSHOVERCONTENT="$1" PUSHOVER_API="$(Conf_Parameters check NOTIFICATIONS_PUSHOVER_API)" PUSHOVER_USERKEY="$(Conf_Parameters check NOTIFICATIONS_PUSHOVER_USERKEY)" if [ -z "$PUSHOVER_API" ] || [ -z "$PUSHOVER_USERKEY" ]; then Print_Output false "No Pushover API or UserKey specified" "$ERR" return 1 fi /usr/sbin/curl -fsL --retry 3 --connect-timeout 15 --output /dev/null --form-string "token=$PUSHOVER_API" \ --form-string "user=$PUSHOVER_USERKEY" --form-string "message=$PUSHOVERCONTENT" https://api.pushover.net/1/messages.json if [ $? -eq 0 ]; then echo "" Print_Output false "Pushover sent successfully" "$PASS" return 0 else echo "" Print_Output true "Pushover failed to send" "$ERR" return 1 fi } SendHealthcheckPing(){ NOTIFICATIONS_HEALTHCHECK_UUID="$(Conf_Parameters check NOTIFICATIONS_HEALTHCHECK_UUID)" TESTFAIL="" if [ "$1" = "Fail" ]; then TESTFAIL="/fail" fi /usr/sbin/curl -fsL --retry 3 --connect-timeout 15 --output /dev/null "https://hc-ping.com/${NOTIFICATIONS_HEALTHCHECK_UUID}${TESTFAIL}" if [ $? -eq 0 ]; then echo "" Print_Output false "Healthcheck ping sent successfully" "$PASS" return 0 else echo "" Print_Output true "Healthcheck ping failed to send" "$ERR" return 1 fi } SendToInfluxDB(){ TIMESTAMP="$1" PING="$2" JITTER="$3" LINEQUAL="$4" NOTIFICATIONS_INFLUXDB_HOST="$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_HOST)" NOTIFICATIONS_INFLUXDB_PORT="$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_PORT)" NOTIFICATIONS_INFLUXDB_DB="$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_DB)" NOTIFICATIONS_INFLUXDB_VERSION="$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_VERSION)" if [ "$NOTIFICATIONS_INFLUXDB_VERSION" = "1.8" ]; then INFLUX_AUTHHEADER="$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_USERNAME):$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_PASSWORD)" elif [ "$NOTIFICATIONS_INFLUXDB_VERSION" = "2.0" ]; then INFLUX_AUTHHEADER="$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_APITOKEN)" fi /usr/sbin/curl -fsL --retry 3 --connect-timeout 15 --output /dev/null -XPOST "http://$NOTIFICATIONS_INFLUXDB_HOST:$NOTIFICATIONS_INFLUXDB_PORT/api/v2/write?bucket=$NOTIFICATIONS_INFLUXDB_DB&precision=s" \ --header "Authorization: Token $INFLUX_AUTHHEADER" --header "Accept-Encoding: gzip" \ --data-raw "ping value=$PING $TIMESTAMP jitter value=$JITTER $TIMESTAMP linequality value=$LINEQUAL $TIMESTAMP" if [ $? -eq 0 ]; then echo "" Print_Output false "Data sent to InfluxDB successfully" "$PASS" return 0 else echo "" Print_Output true "Data failed to send to InfluxDB" "$ERR" return 1 fi } ToggleNotificationTypes(){ case "$1" in enable) sed -i 's/^'"$2"'=.*$/'"$2"'=true/' "$SCRIPT_CONF" ;; disable) sed -i 's/^'"$2"'=.*$/'"$2"'=false/' "$SCRIPT_CONF" ;; check) NOTIFICATION_SETTING="$(Conf_Parameters check "$2")" if [ "$NOTIFICATION_SETTING" = "true" ]; then return 0; else return 1; fi ;; esac } Conf_Parameters(){ case "$1" in update) case "$2" in "PingThreshold") sed -i 's/^NOTIFICATIONS_PINGTHRESHOLD_VALUE=.*$/NOTIFICATIONS_PINGTHRESHOLD_VALUE='"$3"'/' "$SCRIPT_CONF" ;; "JitterThreshold") sed -i 's/^NOTIFICATIONS_JITTERTHRESHOLD_VALUE=.*$/NOTIFICATIONS_JITTERTHRESHOLD_VALUE='"$3"'/' "$SCRIPT_CONF" ;; "LineQualityThreshold") sed -i 's/^NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE=.*$/NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE='"$3"'/' "$SCRIPT_CONF" ;; "HealthcheckUUID") sed -i 's/^NOTIFICATIONS_HEALTHCHECK_UUID=.*$/NOTIFICATIONS_HEALTHCHECK_UUID='"$3"'/' "$SCRIPT_CONF" ;; "Webhook Target") NOTIFICATIONS_WEBHOOK_LIST="$(Webhook_Targets check),$3" NOTIFICATIONS_WEBHOOK_LIST="$(echo "$NOTIFICATIONS_WEBHOOK_LIST" | sed 's~,,~,~g;s~,$~~;s~^,~~')" sed -i 's~^NOTIFICATIONS_WEBHOOK_LIST=.*$~NOTIFICATIONS_WEBHOOK_LIST='"$NOTIFICATIONS_WEBHOOK_LIST"'~' "$SCRIPT_CONF" ;; "Pushover Device") NOTIFICATIONS_PUSHOVER_LIST="$(Pushover_Devices check),$3" NOTIFICATIONS_PUSHOVER_LIST="$(echo "$NOTIFICATIONS_PUSHOVER_LIST" | sed 's~,,~,~g;s~,$~~;s~^,~~')" sed -i 's~^NOTIFICATIONS_PUSHOVER_LIST=.*$~NOTIFICATIONS_PUSHOVER_LIST='"$NOTIFICATIONS_PUSHOVER_LIST"'~' "$SCRIPT_CONF" ;; "Pushover API Token") sed -i 's/^NOTIFICATIONS_PUSHOVER_API=.*$/NOTIFICATIONS_PUSHOVER_API='"$3"'/' "$SCRIPT_CONF" ;; "Pushover User Key") sed -i 's/^NOTIFICATIONS_PUSHOVER_USERKEY=.*$/NOTIFICATIONS_PUSHOVER_USERKEY='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB Host") sed -i 's/^NOTIFICATIONS_INFLUXDB_HOST=.*$/NOTIFICATIONS_INFLUXDB_HOST='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB Port") sed -i 's/^NOTIFICATIONS_INFLUXDB_PORT=.*$/NOTIFICATIONS_INFLUXDB_PORT='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB Database") sed -i 's/^NOTIFICATIONS_INFLUXDB_DB=.*$/NOTIFICATIONS_INFLUXDB_DB='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB Version") sed -i 's/^NOTIFICATIONS_INFLUXDB_VERSION=.*$/NOTIFICATIONS_INFLUXDB_VERSION='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB Username") sed -i 's/^NOTIFICATIONS_INFLUXDB_USERNAME=.*$/NOTIFICATIONS_INFLUXDB_USERNAME='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB Password") sed -i 's/^NOTIFICATIONS_INFLUXDB_PASSWORD=.*$/NOTIFICATIONS_INFLUXDB_PASSWORD='"$3"'/' "$SCRIPT_CONF" ;; "InfluxDB API Token") sed -i 's/^NOTIFICATIONS_INFLUXDB_APITOKEN=.*$/NOTIFICATIONS_INFLUXDB_APITOKEN='"$3"'/' "$SCRIPT_CONF" ;; esac ;; clear) case "$2" in "Webhook Target") sed -i 's~^NOTIFICATIONS_WEBHOOK_LIST=.*$~NOTIFICATIONS_WEBHOOK_LIST=~' "$SCRIPT_CONF" ;; "Pushover Device") sed -i 's~^NOTIFICATIONS_PUSHOVER_LIST=.*$~NOTIFICATIONS_PUSHOVER_LIST=~' "$SCRIPT_CONF" ;; esac ;; check) NOTIFICATION_SETTING="$(grep "$2=" "$SCRIPT_CONF" | cut -f2 -d"=")" echo "$NOTIFICATION_SETTING" ;; esac } Validate_Float(){ if echo "$1" | /bin/grep -oq "^[0-9]*\.\?[0-9]\?[0-9]$"; then return 0 else return 1 fi } Notification_String(){ NOTIFICATION_STRING="" while true; do printf "\\n${BOLD}Enter $1:${CLEARFORMAT} " read -r NOTIFICATION_STRING if [ "$NOTIFICATION_STRING" = "e" ]; then NOTIFICATION_STRING="" break else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) if [ "$1" = "To name" ]; then sed -i 's/^TO_NAME=.*$/TO_NAME='"$NOTIFICATION_STRING"'/' "$EMAIL_CONF" elif [ "$1" = "Username" ]; then sed -i 's/^USERNAME=.*$/USERNAME='"$NOTIFICATION_STRING"'/' "$EMAIL_CONF" else Conf_Parameters update "$1" "$NOTIFICATION_STRING" fi break ;; *) : ;; esac fi done } Notification_Number(){ NOTIFICATION_NUMBER="" while true; do printf "\\n${BOLD}Enter $1:${CLEARFORMAT} " read -r NOTIFICATION_NUMBER if [ "$NOTIFICATION_NUMBER" = "e" ]; then NOTIFICATION_NUMBER="" break elif ! Validate_Number "$NOTIFICATION_NUMBER"; then printf "\\n${ERR}Please enter a number${CLEARFORMAT}\\n" else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) if [ "$1" = "Port" ]; then sed -i 's/^PORT=.*$/PORT="'"$NOTIFICATION_NUMBER"'"/' "$EMAIL_CONF" else Conf_Parameters update "$1" "$NOTIFICATION_NUMBER" fi break ;; *) : ;; esac fi done } Notification_Float(){ NOTIFICATION_FLOAT="" while true; do printf "\\n${BOLD}Enter $1:${CLEARFORMAT} " read -r NOTIFICATION_FLOAT if [ "$NOTIFICATION_FLOAT" = "e" ]; then NOTIFICATION_FLOAT="" break elif ! Validate_Float "$NOTIFICATION_FLOAT"; then printf "\\n${ERR}Please enter a number${CLEARFORMAT}\\n" else printf "${BOLD}${WARN}Is this correct? (y/n):${CLEARFORMAT} " read -r CONFIRM_INPUT case "$CONFIRM_INPUT" in y|Y) Conf_Parameters update "$1" "$NOTIFICATION_FLOAT" break ;; *) : ;; esac fi done } TriggerNotifications(){ TRIGGERTYPE="$1" DATETIME="$2" if [ "$TRIGGERTYPE" = "PingTest" ]; then PING="$3" JITTER="$4" LINEQUAL="$5" TIMESTAMP="$6" elif [ "$TRIGGERTYPE" = "PingThreshold" ]; then PING="$3" THRESHOLD="$4" elif [ "$TRIGGERTYPE" = "JitterThreshold" ]; then JITTER="$3" THRESHOLD="$4" elif [ "$TRIGGERTYPE" = "LineQualityThreshold" ]; then LINEQUAL="$3" THRESHOLD="$4" fi NOTIFICATIONMETHODS="$(NotificationMethods check "$TRIGGERTYPE")" IFS=$',' for NOTIFICATIONMETHOD in $NOTIFICATIONMETHODS; do NOTIFICATIONMETHOD_SETTING="$(echo "NOTIFICATIONS_${NOTIFICATIONMETHOD}" | tr "a-z" "A-Z")" if ToggleNotificationTypes check "$NOTIFICATIONMETHOD_SETTING"; then if [ "$NOTIFICATIONMETHOD" = "Email" ]; then NOTIFICATIONS_EMAIL_LIST="$(Email_Recipients check)" if [ -z "$NOTIFICATIONS_EMAIL_LIST" ]; then if [ "$TRIGGERTYPE" = "PingTest" ]; then SendEmail "Ping test result from $SCRIPT_NAME - $DATETIME" "

Ping: $PING
Jitter: $JITTER
Line Quality: $LINEQUAL

" elif [ "$TRIGGERTYPE" = "PingThreshold" ]; then SendEmail "Ping threshold alert from $SCRIPT_NAME - $DATETIME" "

Ping $PING exceeds threshold of $THRESHOLD

" elif [ "$TRIGGERTYPE" = "JitterThreshold" ]; then SendEmail "Jitter threshold alert from $SCRIPT_NAME - $DATETIME" "

Jitter $JITTER exceeds threshold of $THRESHOLD

" elif [ "$TRIGGERTYPE" = "LineQualityThreshold" ]; then SendEmail "Line quality threshold alert from $SCRIPT_NAME - $DATETIME" "

Line quality $LINEQUAL exceeds threshold of $THRESHOLD

" fi else for EMAIL in $NOTIFICATIONS_EMAIL_LIST; do if [ "$TRIGGERTYPE" = "PingTest" ]; then SendEmail "Ping test result from $SCRIPT_NAME - $DATETIME" "

Ping: $PING
Jitter: $JITTER
Line Quality: $LINEQUAL

" "$EMAIL" elif [ "$TRIGGERTYPE" = "PingThreshold" ]; then SendEmail "Ping threshold alert from $SCRIPT_NAME - $DATETIME" "

Ping $PING exceeds threshold of $THRESHOLD

" "$EMAIL" elif [ "$TRIGGERTYPE" = "JitterThreshold" ]; then SendEmail "Jitter threshold alert from $SCRIPT_NAME - $DATETIME" "

Jitter $JITTER exceeds threshold of $THRESHOLD

" "$EMAIL" elif [ "$TRIGGERTYPE" = "LineQualityThreshold" ]; then SendEmail "Line quality threshold alert from $SCRIPT_NAME - $DATETIME" "

Line quality $LINEQUAL exceeds threshold of $THRESHOLD

" "$EMAIL" fi done fi elif [ "$NOTIFICATIONMETHOD" = "Webhook" ]; then NOTIFICATIONS_WEBHOOK_LIST="$(Webhook_Targets check)" for WEBHOOK in $NOTIFICATIONS_WEBHOOK_LIST; do if [ "$TRIGGERTYPE" = "PingTest" ]; then SendWebhook "Ping test result from $SCRIPT_NAME - $DATETIME\n\nPing: $PING\nJitter: $JITTER\nLine Quality: $LINEQUAL" "$WEBHOOK" elif [ "$TRIGGERTYPE" = "PingThreshold" ]; then SendWebhook "Ping threshold alert from $SCRIPT_NAME - $DATETIME\n\nPing $PING exceeds threshold of $THRESHOLD" "$WEBHOOK" elif [ "$TRIGGERTYPE" = "JitterThreshold" ]; then SendWebhook "Jitter threshold alert from $SCRIPT_NAME - $DATETIME\n\nJitter $JITTER exceeds threshold of $THRESHOLD" "$WEBHOOK" elif [ "$TRIGGERTYPE" = "LineQualityThreshold" ]; then SendWebhook "Line quality threshold alert from $SCRIPT_NAME - $DATETIME\n\nLine quality $LINEQUAL exceeds threshold of $THRESHOLD" "$WEBHOOK" fi done elif [ "$NOTIFICATIONMETHOD" = "Pushover" ]; then if [ "$TRIGGERTYPE" = "PingTest" ]; then SendPushover "Ping test result from $SCRIPT_NAME - $DATETIME"$'\n'$'\n'"Ping: $PING"$'\n'"Jitter: $JITTER"$'\n'"Line Quality: $LINEQUAL" elif [ "$TRIGGERTYPE" = "PingThreshold" ]; then SendPushover "Ping threshold alert from $SCRIPT_NAME - $DATETIME"$'\n'$'\n'"Ping $PING exceeds threshold of $THRESHOLD" elif [ "$TRIGGERTYPE" = "JitterThreshold" ]; then SendPushover "Jitter threshold alert from $SCRIPT_NAME - $DATETIME"$'\n'$'\n'"Jitter $JITTER exceeds threshold of $THRESHOLD" elif [ "$TRIGGERTYPE" = "LineQualityThreshold" ]; then SendPushover "Line quality threshold alert from $SCRIPT_NAME - $DATETIME"$'\n'$'\n'"Line quality $LINEQUAL exceeds threshold of $THRESHOLD" fi elif [ "$NOTIFICATIONMETHOD" = "Custom" ]; then FILES="$USER_SCRIPT_DIR/*.sh" for f in $FILES; do if [ -f "$f" ]; then Print_Output true "Executing user script: $f" if [ "$TRIGGERTYPE" = "PingTest" ]; then sh "$f" "$TRIGGERTYPE" "$DATETIME" "$PING" "$JITTER" "$LINEQUAL" elif [ "$TRIGGERTYPE" = "PingThreshold" ]; then sh "$f" "$TRIGGERTYPE" "$DATETIME" "$PING" "$THRESHOLD" elif [ "$TRIGGERTYPE" = "JitterThreshold" ]; then sh "$f" "$TRIGGERTYPE" "$DATETIME" "$JITTER" "$THRESHOLD" elif [ "$TRIGGERTYPE" = "LineQualityThreshold" ]; then sh "$f" "$TRIGGERTYPE" "$DATETIME" "$LINEQUAL" "$THRESHOLD" fi fi done fi fi done unset IFS if ToggleNotificationTypes check NOTIFICATIONS_HEALTHCHECK && [ "$TRIGGERTYPE" = "PingTest" ]; then NOTIFICATIONS_HEALTHCHECK_UUID="$(Conf_Parameters check NOTIFICATIONS_HEALTHCHECK_UUID)" TESTFAIL="" if [ "$(echo "$LINEQUAL" | cut -f1 -d' ' | cut -f1 -d'.')" -eq 0 ]; then SendHealthcheckPing "Fail" else SendHealthcheckPing "Pass" fi fi if ToggleNotificationTypes check NOTIFICATIONS_INFLUXDB && [ "$TRIGGERTYPE" = "PingTest" ]; then SendToInfluxDB "$TIMESTAMP" "$(echo "$PING" | cut -f1 -d' ')" "$(echo "$JITTER" | cut -f1 -d' ')" "$(echo "$LINEQUAL" | cut -f1 -d' ')" fi } Menu_EmailNotifications(){ while true; do Email_ConfExists ScriptHeader NOTIFICATIONS_EMAIL="" if ToggleNotificationTypes check NOTIFICATIONS_EMAIL; then NOTIFICATIONS_EMAIL="${PASS}Enabled"; else NOTIFICATIONS_EMAIL="${ERR}Disabled"; fi NOTIFICATIONS_EMAIL_LIST="$(Email_Recipients check)" if [ "$NOTIFICATIONS_EMAIL_LIST" = "" ]; then NOTIFICATIONS_EMAIL_LIST="Generic To Address will be used" fi printf "1. Toggle email notifications (subject to type configuration)\\n Currently: ${BOLD}${NOTIFICATIONS_EMAIL}${CLEARFORMAT}\\n\\n" printf "2. Set override list of email addresses for %s\\n Currently: ${SETTING}${NOTIFICATIONS_EMAIL_LIST}${CLEARFORMAT}\\n\\n" "$SCRIPT_NAME" printf "\\n${BOLD}${UNDERLINE}Generic Email Configuration${CLEARFORMAT}\\n" Email_Header printf "c1. Set From Address Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$FROM_ADDRESS" printf "c2. Set To Address Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$TO_ADDRESS" printf "c3. Set To name Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$TO_NAME" printf "c4. Set Username Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$USERNAME" printf "c5. Set Password\\n" printf "c6. Set Friendly router name Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$FRIENDLY_ROUTER_NAME" printf "c7. Set SMTP address Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$SMTP" printf "c8. Set SMTP port Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$PORT" printf "c9. Set SMTP protocol Currently: ${SETTING}%s${CLEARFORMAT}\\n" "$PROTOCOL" printf "c10. Set SSL requirement Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$SSL_FLAG" printf "cs. Send a test email\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" printf "Choose an option: " read -r emailmenu case "$emailmenu" in 1) if ToggleNotificationTypes check NOTIFICATIONS_EMAIL; then ToggleNotificationTypes disable NOTIFICATIONS_EMAIL else ToggleNotificationTypes enable NOTIFICATIONS_EMAIL fi ;; 2) Email_Recipients update ;; c1) Email_EmailAddress From ;; c2) Email_EmailAddress To ;; c3) Notification_String "To name" ;; c4) Notification_String Username ;; c5) Email_Password ;; c6) Email_RouterName ;; c7) Email_Server ;; c8) Notification_Number Port ;; c9) Email_Protocol ;; c10) Email_SSL ;; cs) NOTIFICATIONS_EMAIL_LIST="$(Email_Recipients check)" if [ -z "$NOTIFICATIONS_EMAIL_LIST" ]; then SendEmail "Test email - $(/bin/date +"%c")" "This is a test email!" else for EMAIL in $NOTIFICATIONS_EMAIL_LIST; do SendEmail "Test email - $(/bin/date +"%c")" "This is a test email!" "$EMAIL" done fi printf "\\n" PressEnter ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } Menu_WebhookNotifications(){ while true; do ScriptHeader NOTIFICATIONS_WEBHOOK="" if ToggleNotificationTypes check NOTIFICATIONS_WEBHOOK; then NOTIFICATIONS_WEBHOOK="${PASS}Enabled"; else NOTIFICATIONS_WEBHOOK="${ERR}Disabled"; fi NOTIFICATIONS_WEBHOOK_LIST="$(Webhook_Targets check | sed 's~,~\n~g')" printf "1. Toggle Discord webhook notifications (subject to type configuration)\\n Currently: ${BOLD}${NOTIFICATIONS_WEBHOOK}${CLEARFORMAT}\\n\\n" printf "2. Set list of Discord webhook URLs for %s\\n Current webhooks:\\n ${SETTING}${NOTIFICATIONS_WEBHOOK_LIST}${CLEARFORMAT}\\n\\n" "$SCRIPT_NAME" printf "cs. Send a test webhook notification\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" printf "Choose an option: " read -r webhookmenu case "$webhookmenu" in 1) if ToggleNotificationTypes check NOTIFICATIONS_WEBHOOK; then ToggleNotificationTypes disable NOTIFICATIONS_WEBHOOK else ToggleNotificationTypes enable NOTIFICATIONS_WEBHOOK fi ;; 2) Webhook_Targets update ;; cs) NOTIFICATIONS_WEBHOOK_LIST="$(Webhook_Targets check)" if [ -z "$NOTIFICATIONS_WEBHOOK_LIST" ]; then printf "\\n" Print_Output false "No Webhook URL specified" "$ERR" fi IFS=$',' for WEBHOOK in $NOTIFICATIONS_WEBHOOK_LIST; do SendWebhook "$(/bin/date +"%c")\n\nThis is a test webhook message!" "$WEBHOOK" done unset IFS printf "\\n" PressEnter ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } Menu_PushoverNotifications(){ while true; do ScriptHeader NOTIFICATIONS_PUSHOVER="" if ToggleNotificationTypes check NOTIFICATIONS_PUSHOVER; then NOTIFICATIONS_PUSHOVER="${PASS}Enabled"; else NOTIFICATIONS_PUSHOVER="${ERR}Disabled"; fi NOTIFICATIONS_PUSHOVER_LIST="$(Pushover_Devices check)" if [ -z "$NOTIFICATIONS_PUSHOVER_LIST" ]; then NOTIFICATIONS_PUSHOVER_LIST="All devices" fi printf "1. Toggle Pushover notifications (subject to type configuration)\\n Currently: ${BOLD}${NOTIFICATIONS_PUSHOVER}${CLEARFORMAT}\\n\\n" printf "\\n${BOLD}${UNDERLINE}Pushover Configuration${CLEARFORMAT}\\n" printf "c1. Set API token\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_PUSHOVER_API)" printf "c2. Set User key\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_PUSHOVER_USERKEY)" printf "c3. Set list of Pushover devices for %s\\n Current devices: ${SETTING}${NOTIFICATIONS_PUSHOVER_LIST}${CLEARFORMAT}\\n\\n" "$SCRIPT_NAME" printf "cs. Send a test pushover notification\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" printf "Choose an option: " read -r pushovermenu case "$pushovermenu" in 1) if ToggleNotificationTypes check NOTIFICATIONS_PUSHOVER; then ToggleNotificationTypes disable NOTIFICATIONS_PUSHOVER else ToggleNotificationTypes enable NOTIFICATIONS_PUSHOVER fi ;; c1) Notification_String "Pushover API Token" ;; c2) Notification_String "Pushover User Key" ;; c3) Pushover_Devices update ;; cs) SendPushover "$(/bin/date +"%c")"$'\n'$'\n'"This is a test pushover message!" printf "\\n" PressEnter ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } CustomAction_Info(){ if [ -z "$1" ]; then printf "\\n${BOLD}${UNDERLINE}Scripts are passed arguments, which change depending on the type of trigger${CLEARFORMAT}\\n\\n" printf "${BOLD}${UNDERLINE}Trigger Argument1 Argument2 Argument3 Argument4 Argument5${CLEARFORMAT}\\n" printf "${BOLD}Tests${CLEARFORMAT}"' PingTest FormattedDateTime "Ping ms" "Jitter ms" "Latency %%"'"\\n" printf "${BOLD}Ping thresholds${CLEARFORMAT}"' PingThreshold FormattedDateTime "Ping ms" "ThresholdValue ms"'"\\n" printf "${BOLD}Jitter thresholds${CLEARFORMAT}"' JitterThreshold FormattedDateTime "Jitter ms" "ThresholdValue ms"'"\\n" printf "${BOLD}Line Quality thresholds${CLEARFORMAT}"' LineQualityThreshold FormattedDateTime "Latency %%" "ThresholdValue %%"'"\\n\\n" printf "A great example of a custom script would be to leverage the Apprise library ${BOLD}https://github.com/caronc/apprise${CLEARFORMAT}\\n" printf "This library provides easy integration with many notification schemes. See ${BOLD}https://github.com/caronc/apprise#popular-notification-services${CLEARFORMAT}\\n" printf "You can install apprise on your router by running:\\n\\n" printf "${BOLD}opkg install python3 python3-pip && /opt/bin/python3 -m pip install --upgrade pip${CLEARFORMAT}\\n\\n" printf "Apprise can then be leveraged at the command line as shown here: ${BOLD}https://github.com/caronc/apprise#command-line${CLEARFORMAT}\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" fi { printf "Scripts are passed arguments, which change depending on the type of trigger\\n\\n" printf "Trigger Argument1 Argument2 Argument3 Argument4 Argument5\\n" printf "Tests"' PingTest FormattedDateTime "Ping ms" "Jitter ms" "Latency %%"'"\\n" printf "Ping thresholds"' PingThreshold FormattedDateTime "Ping ms" "ThresholdValue ms"'"\\n" printf "Jitter thresholds"' JitterThreshold FormattedDateTime "Jitter ms" "ThresholdValue ms"'"\\n" printf "Line Quality thresholds"' LineQualityThreshold FormattedDateTime "Latency %%" "ThresholdValue %%"'"\\n\\n" printf "A great example of a custom script would be to leverage the Apprise library https://github.com/caronc/apprise\\n" printf "This library provides easy integration with many notification schemes. See https://github.com/caronc/apprise#popular-notification-services\\n" printf "You can install apprise on your router by running:\\n\\n" printf "opkg install python3 python3-pip && /opt/bin/python3 -m pip install --upgrade pip\\n\\n" printf "Apprise can then be leveraged at the command line as shown here: https://github.com/caronc/apprise#command-line\\n\\n" } > "$SCRIPT_STORAGE_DIR/.customactioninfo" } CustomAction_List(){ if [ -z "$1" ]; then FILES="$USER_SCRIPT_DIR/*.sh" for f in $FILES; do if [ -f "$f" ]; then printf "${SETTING}%s${CLEARFORMAT}\\n" "$f" fi done fi printf "Scripts that will be run:\\n" > "$SCRIPT_STORAGE_DIR/.customactionlist" FILES="$USER_SCRIPT_DIR/*.sh" for f in $FILES; do if [ -f "$f" ]; then printf "%s\\n" "$f" >> "$SCRIPT_STORAGE_DIR/.customactionlist" fi done } Menu_CustomActions(){ while true; do ScriptHeader NOTIFICATIONS_CUSTOM="" if ToggleNotificationTypes check NOTIFICATIONS_CUSTOM; then NOTIFICATIONS_CUSTOM="${PASS}Enabled"; else NOTIFICATIONS_CUSTOM="${ERR}Disabled"; fi printf "1. Toggle custom actions and scripts (subject to type configuration)\\n Currently: ${BOLD}${NOTIFICATIONS_CUSTOM}${CLEARFORMAT}\\n\\n" printf "Scripts that will be run:\\n" if [ -z "$(ls -A "$USER_SCRIPT_DIR")" ]; then printf "${SETTING}No scripts found in ${USER_SCRIPT_DIR}${CLEARFORMAT}\\n" else CustomAction_List fi CustomAction_Info printf "Choose an option: " read -r custommenu case "$custommenu" in 1) if ToggleNotificationTypes check NOTIFICATIONS_CUSTOM; then ToggleNotificationTypes disable NOTIFICATIONS_CUSTOM else ToggleNotificationTypes enable NOTIFICATIONS_CUSTOM fi ;; cs) if [ -z "$(ls -A "$USER_SCRIPT_DIR")" ]; then printf "\\n${SETTING}No scripts found in ${USER_SCRIPT_DIR}${CLEARFORMAT}\\n\\n" PressEnter else printf "\\n" FILES="$USER_SCRIPT_DIR/*.sh" for f in $FILES; do if [ -f "$f" ]; then Print_Output false "Executing user script: $f" sh "$f" "$(/bin/date +%c)" "30 ms" "15 ms" "90%" fi done PressEnter fi ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } Menu_HealthcheckNotifications(){ while true; do ScriptHeader NOTIFICATIONS_HEALTHCHECK="" if ToggleNotificationTypes check NOTIFICATIONS_HEALTHCHECK; then NOTIFICATIONS_HEALTHCHECK="${PASS}Enabled"; else NOTIFICATIONS_HEALTHCHECK="${ERR}Disabled"; fi printf "1. Toggle healthchecks.io\\n Currently: ${BOLD}${NOTIFICATIONS_HEALTHCHECK}${CLEARFORMAT}\\n\\n" printf "\\n${BOLD}${UNDERLINE}Healthcheck Configuration${CLEARFORMAT}\\n\\n" printf "c1. Set Healthcheck UUID\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_HEALTHCHECK_UUID)" printf "Cron schedule for Healthchecks.io configuration: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(cru l | grep "$SCRIPT_NAME" | cut -f1-5 -d' ')" printf "cs. Send a test healthcheck notification\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" printf "Choose an option: " read -r healthcheckmenu case "$healthcheckmenu" in 1) if ToggleNotificationTypes check NOTIFICATIONS_HEALTHCHECK; then ToggleNotificationTypes disable NOTIFICATIONS_HEALTHCHECK else ToggleNotificationTypes enable NOTIFICATIONS_HEALTHCHECK fi ;; c1) Notification_String HealthcheckUUID ;; cs) SendHealthcheckPing "Pass" printf "\\n" PressEnter ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } Menu_InfluxDB(){ while true; do ScriptHeader NOTIFICATIONS_INFLUXDB="" if ToggleNotificationTypes check NOTIFICATIONS_INFLUXDB; then NOTIFICATIONS_INFLUXDB="${PASS}Enabled"; else NOTIFICATIONS_INFLUXDB="${ERR}Disabled"; fi printf "1. Toggle InfluxDB exporting\\n Currently: ${BOLD}${NOTIFICATIONS_INFLUXDB}${CLEARFORMAT}\\n\\n" printf "\\n${BOLD}${UNDERLINE}InfluxDB Configuration${CLEARFORMAT}\\n\\n" printf "c1. Set InfluxDB Host\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_HOST)" printf "c2. Set InfluxDB Port\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_PORT)" printf "c3. Set InfluxDB Database\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_DB)" printf "c4. Set InfluxDB Version\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_VERSION)" printf "c5. Set InfluxDB Username (v1.8+ only)\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_USERNAME)" printf "c6. Set InfluxDB Password (v1.8+ only)\\n\\n" printf "c7. Set InfluxDB API Token (v2.x only)\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_APITOKEN)" printf "cs. Send test data to InfluxDB\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" printf "Choose an option: " read -r healthcheckmenu case "$healthcheckmenu" in 1) if ToggleNotificationTypes check NOTIFICATIONS_INFLUXDB; then ToggleNotificationTypes disable NOTIFICATIONS_INFLUXDB else ToggleNotificationTypes enable NOTIFICATIONS_INFLUXDB fi ;; c1) Notification_String "InfluxDB Host" ;; c2) Notification_Number "InfluxDB Port" ;; c3) Notification_String "InfluxDB Database" ;; c4) if [ "$(Conf_Parameters check NOTIFICATIONS_INFLUXDB_VERSION)" = "1.8" ]; then Conf_Parameters update "InfluxDB Version" "2.0" else Conf_Parameters update "InfluxDB Version" "1.8" fi ;; c5) Notification_String "InfluxDB Username" ;; c6) Notification_String "InfluxDB Password" ;; c7) Notification_String "InfluxDB API Token" ;; cs) SendToInfluxDB "$(/bin/date +%s)" 30 15 90 printf "\\n" PressEnter ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } Menu_Notifications(){ while true; do ScriptHeader printf "${BOLD}${UNDERLINE}Notification Types${CLEARFORMAT}\\n" printf "1. Ping test\\n Current methods: ${SETTING}$(NotificationMethods check PingTest)${CLEARFORMAT}\\n\\n" printf "2. Ping threshold (values above this will trigger an alert)\\n Current threshold: ${SETTING}$(Conf_Parameters check NOTIFICATIONS_PINGTHRESHOLD_VALUE) ms${CLEARFORMAT}\\n Current methods: ${SETTING}$(NotificationMethods check PingThreshold)${CLEARFORMAT}\\n\\n" printf "3. Jitter threshold (values above this will trigger an alert)\\n Current threshold: ${SETTING}$(Conf_Parameters check NOTIFICATIONS_JITTERTHRESHOLD_VALUE) ms${CLEARFORMAT}\\n Current methods: ${SETTING}$(NotificationMethods check JitterThreshold)${CLEARFORMAT}\\n\\n" printf "4. Line Quality threshold (values below this will trigger an alert)\\n Current threshold: ${SETTING}$(Conf_Parameters check NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE) %%${CLEARFORMAT}\\n Current methods: ${SETTING}$(NotificationMethods check LineQualityThreshold)${CLEARFORMAT}\\n\\n" printf "\\n${BOLD}${UNDERLINE}Notification Methods and Integrations${CLEARFORMAT}\\n" NOTIFICATION_SETTING="" if ToggleNotificationTypes check NOTIFICATIONS_EMAIL; then NOTIFICATION_SETTING="${PASS}Enabled"; else NOTIFICATION_SETTING="${ERR}Disabled"; fi printf "em. Email (shared with other addons/scripts e.g. Diversion)\\n Currently: ${BOLD}${NOTIFICATION_SETTING}${CLEARFORMAT}\\n\\n" if ToggleNotificationTypes check NOTIFICATIONS_WEBHOOK; then NOTIFICATION_SETTING="${PASS}Enabled"; else NOTIFICATION_SETTING="${ERR}Disabled"; fi printf "wb. Discord webhook\\n Currently: ${BOLD}${NOTIFICATION_SETTING}${CLEARFORMAT}\\n\\n" if ToggleNotificationTypes check NOTIFICATIONS_PUSHOVER; then NOTIFICATION_SETTING="${PASS}Enabled"; else NOTIFICATION_SETTING="${ERR}Disabled"; fi printf "po. Pushover\\n Currently: ${BOLD}${NOTIFICATION_SETTING}${CLEARFORMAT}\\n\\n" if ToggleNotificationTypes check NOTIFICATIONS_CUSTOM; then NOTIFICATION_SETTING="${PASS}Enabled"; else NOTIFICATION_SETTING="${ERR}Disabled"; fi printf "ca. Custom actions and scripts\\n Currently: ${BOLD}${NOTIFICATION_SETTING}${CLEARFORMAT}\\n\\n" if ToggleNotificationTypes check NOTIFICATIONS_HEALTHCHECK; then NOTIFICATION_SETTING="${PASS}Enabled"; else NOTIFICATION_SETTING="${ERR}Disabled"; fi printf "hc. Healthchecks.io\\n Currently: ${BOLD}${NOTIFICATION_SETTING}${CLEARFORMAT}\\n\\n" if ToggleNotificationTypes check NOTIFICATIONS_INFLUXDB; then NOTIFICATION_SETTING="${PASS}Enabled"; else NOTIFICATION_SETTING="${ERR}Disabled"; fi printf "id. InfluxDB exporting\\n Currently: ${BOLD}${NOTIFICATION_SETTING}${CLEARFORMAT}\\n\\n" printf "e. Go back\\n\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" printf "Choose an option: " read -r notificationsmenu case "$notificationsmenu" in 1) NotificationMethods update PingTest ;; 2) NotificationMethods update PingThreshold ;; 3) NotificationMethods update JitterThreshold ;; 4) NotificationMethods update LineQualityThreshold ;; em) Menu_EmailNotifications ;; wb) Menu_WebhookNotifications ;; po) Menu_PushoverNotifications ;; ca) Menu_CustomActions ;; hc) Menu_HealthcheckNotifications ;; id) Menu_InfluxDB ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac done } NotificationMethods(){ case "$1" in update) while true; do ScriptHeader printf "${BOLD}${UNDERLINE}${2}${CLEARFORMAT}\\n\\n" if [ "$2" = "PingThreshold" ] || [ "$2" = "JitterThreshold" ] || [ "$2" = "LineQualityThreshold" ]; then case "$2" in PingThreshold) PARAMETERNAME="NOTIFICATIONS_PINGTHRESHOLD_VALUE" UNIT="ms" ;; JitterThreshold) PARAMETERNAME="NOTIFICATIONS_JITTERTHRESHOLD_VALUE" UNIT="ms" ;; LineQualityThreshold) PARAMETERNAME="NOTIFICATIONS_LINEQUALITYTHRESHOLD_VALUE" UNIT="%%" ;; esac printf "c1. Set threshold value - Currently: ${SETTING}$(Conf_Parameters check "$PARAMETERNAME") $UNIT${CLEARFORMAT}\\n\\n" fi printf "Please choose the notification methods to enable\\n" printf "${BOLD}Currently enabled: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(NotificationMethods check "$2")" printf "1. Email\\n" printf "2. Webhook\\n" printf "3. Pushover\\n" printf "4. Custom\\n" printf "5. None\\n\\n" printf "e. Go back\\n\\n" printf "Choose an option: " SETTINGNAME="" SETTINGVALUE="" read -r methodsmenu case "$methodsmenu" in 1) SETTINGVALUE="Email" ;; 2) SETTINGVALUE="Webhook" ;; 3) SETTINGVALUE="Pushover" ;; 4) SETTINGVALUE="Custom" ;; 5) SETTINGVALUE="None" ;; c1) Notification_Float "$2" ;; e) break ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" PressEnter ;; esac if [ "$methodsmenu" != "e" ] && [ "$methodsmenu" != "c1" ]; then case "$2" in PingTest) SETTINGNAME="NOTIFICATIONS_PINGTEST" ;; PingThreshold) SETTINGNAME="NOTIFICATIONS_PINGTHRESHOLD" ;; JitterThreshold) SETTINGNAME="NOTIFICATIONS_JITTERTHRESHOLD" ;; LineQualityThreshold) SETTINGNAME="NOTIFICATIONS_LINEQUALITYTHRESHOLD" ;; esac NOTIFICATION_SETTING="$(Conf_Parameters check "$SETTINGNAME")" if [ "$SETTINGVALUE" = "None" ]; then sed -i 's/^'"$SETTINGNAME"'=.*$/'"$SETTINGNAME"'=None/' "$SCRIPT_CONF" else if echo "$NOTIFICATION_SETTING" | grep -q "$SETTINGVALUE"; then NOTIFICATION_SETTING="$(echo "$NOTIFICATION_SETTING" | sed 's/'"$SETTINGVALUE"'//g;s/,,/,/g;s/,$//;s/^,//')" sed -i 's/^'"$SETTINGNAME"'=.*$/'"$SETTINGNAME"'='"$NOTIFICATION_SETTING"'/' "$SCRIPT_CONF" else NOTIFICATION_SETTING="$(echo "$SETTINGVALUE,$NOTIFICATION_SETTING" | sed 's/None//g;s/,,/,/g;s/,$//;s/^,//')" sed -i 's/^'"$SETTINGNAME"'=.*$/'"$SETTINGNAME"'='"$NOTIFICATION_SETTING"'/' "$SCRIPT_CONF" fi NOTIFICATION_SETTING="$(Conf_Parameters check "$SETTINGNAME")" if [ -z "$NOTIFICATION_SETTING" ]; then sed -i 's/^'"$SETTINGNAME"'=.*$/'"$SETTINGNAME"'=None/' "$SCRIPT_CONF" fi fi fi done ;; check) case "$2" in PingTest) NOTIFICATION_SETTING="$(Conf_Parameters check NOTIFICATIONS_PINGTEST)" echo "$NOTIFICATION_SETTING" ;; PingThreshold) NOTIFICATION_SETTING="$(Conf_Parameters check NOTIFICATIONS_PINGTHRESHOLD)" echo "$NOTIFICATION_SETTING" ;; JitterThreshold) NOTIFICATION_SETTING="$(Conf_Parameters check NOTIFICATIONS_JITTERTHRESHOLD)" echo "$NOTIFICATION_SETTING" ;; LineQualityThreshold) NOTIFICATION_SETTING="$(Conf_Parameters check NOTIFICATIONS_LINEQUALITYTHRESHOLD)" echo "$NOTIFICATION_SETTING" ;; esac ;; esac } ScriptHeader(){ clear printf "\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "${BOLD}## ___ ___ _ __ _ __ _ __ ___ ___ _ __ ##${CLEARFORMAT}\\n" printf "${BOLD}## / __| / _ \ | '_ \ | '_ \ | '_ _ \ / _ \ | '_ \ ##${CLEARFORMAT}\\n" printf "${BOLD}## | (__ | (_) || | | || | | || | | | | || (_) || | | | ##${CLEARFORMAT}\\n" printf "${BOLD}## \___| \___/ |_| |_||_| |_||_| |_| |_| \___/ |_| |_| ##${CLEARFORMAT}\\n" printf "${BOLD}## ##${CLEARFORMAT}\\n" printf "${BOLD}## %s on %-11s ##${CLEARFORMAT}\\n" "$SCRIPT_VERSION" "$ROUTER_MODEL" printf "${BOLD}## ##${CLEARFORMAT}\\n" printf "${BOLD}## https://github.com/jackyaz/connmon ##${CLEARFORMAT}\\n" printf "${BOLD}## ##${CLEARFORMAT}\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" } MainMenu(){ EXCLUDEFROMQOS_MENU="" if [ "$(ExcludeFromQoS check)" = "true" ]; then EXCLUDEFROMQOS_MENU="excluded from"; else EXCLUDEFROMQOS_MENU="included in"; fi AUTOMATIC_ENABLED="" if AutomaticMode check; then AUTOMATIC_ENABLED="${PASS}Enabled"; else AUTOMATIC_ENABLED="${ERR}Disabled"; fi TEST_SCHEDULE="$(TestSchedule check)" if [ "$(echo "$TEST_SCHEDULE" | cut -f2 -d'|' | grep -c "/")" -gt 0 ] && [ "$(echo "$TEST_SCHEDULE" | cut -f3 -d'|')" -eq 0 ]; then TEST_SCHEDULE_MENU="Every $(echo "$TEST_SCHEDULE" | cut -f2 -d'|' | cut -f2 -d'/') hours" elif [ "$(echo "$TEST_SCHEDULE" | cut -f3 -d'|' | grep -c "/")" -gt 0 ] && [ "$(echo "$TEST_SCHEDULE" | cut -f2 -d'|')" = "*" ]; then TEST_SCHEDULE_MENU="Every $(echo "$TEST_SCHEDULE" | cut -f3 -d'|' | cut -f2 -d'/') minutes" else TEST_SCHEDULE_MENU="Hours: $(echo "$TEST_SCHEDULE" | cut -f2 -d'|') - Minutes: $(echo "$TEST_SCHEDULE" | cut -f3 -d'|')" fi if [ "$(echo "$TEST_SCHEDULE" | cut -f1 -d'|')" = "*" ]; then TEST_SCHEDULE_MENU2="Days of week: All" else TEST_SCHEDULE_MENU2="Days of week: $(echo "$TEST_SCHEDULE" | cut -f1 -d'|')" fi printf "WebUI for %s is available at:\\n${SETTING}%s${CLEARFORMAT}\\n\\n" "$SCRIPT_NAME" "$(Get_WebUI_URL)" printf "1. Check connection now\\n\\n" printf "2. Set preferred ping server\\n Currently: ${SETTING}%s${CLEARFORMAT}\\n\\n" "$(PingServer check)" printf "3. Set ping test duration\\n Currently: ${SETTING}%ss${CLEARFORMAT}\\n\\n" "$(PingDuration check)" printf "4. Toggle automatic ping tests\\n Currently: ${BOLD}$AUTOMATIC_ENABLED${CLEARFORMAT}\\n\\n" printf "5. Set schedule for automatic ping tests\\n ${SETTING}%s\\n %s${CLEARFORMAT}\\n\\n" "$TEST_SCHEDULE_MENU" "$TEST_SCHEDULE_MENU2" printf "6. Toggle time output mode\\n Currently ${SETTING}%s${CLEARFORMAT} time values will be used for CSV exports\\n\\n" "$(OutputTimeMode check)" printf "7. Set number of ping test results to show in WebUI\\n Currently: ${SETTING}%s results will be shown${CLEARFORMAT}\\n\\n" "$(LastXResults check)" printf "8. Set number of days data to keep in database\\n Currently: ${SETTING}%s days data will be kept${CLEARFORMAT}\\n\\n" "$(DaysToKeep check)" printf "s. Toggle storage location for stats and config\\n Current location is ${SETTING}%s${CLEARFORMAT} \\n\\n" "$(ScriptStorageLocation check)" printf "q. Toggle exclusion of %s ping tests from QoS\\n Currently %s ping tests are ${SETTING}%s\\e[0m QoS\\n\\n" "$SCRIPT_NAME" "$SCRIPT_NAME" "$EXCLUDEFROMQOS_MENU" printf "n. Configure notifications and integrations for %s\\n\\n" "$SCRIPT_NAME" printf "u. Check for updates\\n" printf "uf. Update %s with latest version (force update)\\n\\n" "$SCRIPT_NAME" printf "cl. View changelog for %s (use q to exit)\\n\\n" "$SCRIPT_NAME" printf "r. Reset %s database / delete all data\\n\\n" "$SCRIPT_NAME" printf "e. Exit %s\\n\\n" "$SCRIPT_NAME" printf "z. Uninstall %s\\n" "$SCRIPT_NAME" printf "\\n" printf "${BOLD}##############################################################${CLEARFORMAT}\\n" printf "\\n" while true; do printf "Choose an option: " read -r menu case "$menu" in 1) printf "\\n" if Check_Lock menu; then Run_PingTest Clear_Lock fi PressEnter break ;; 2) printf "\\n" PingServer update PressEnter break ;; 3) printf "\\n" PingDuration update PressEnter break ;; 4) printf "\\n" if AutomaticMode check; then AutomaticMode disable else AutomaticMode enable fi break ;; 5) printf "\\n" Menu_EditSchedule PressEnter break ;; 6) printf "\\n" if [ "$(OutputTimeMode check)" = "unix" ]; then OutputTimeMode non-unix elif [ "$(OutputTimeMode check)" = "non-unix" ]; then OutputTimeMode unix fi break ;; 7) printf "\\n" LastXResults update PressEnter break ;; 8) printf "\\n" DaysToKeep update PressEnter break ;; s) printf "\\n" if [ "$(ScriptStorageLocation check)" = "jffs" ]; then ScriptStorageLocation usb Create_Symlinks elif [ "$(ScriptStorageLocation check)" = "usb" ]; then ScriptStorageLocation jffs Create_Symlinks fi break ;; q) printf "\\n" if [ "$(ExcludeFromQoS check)" = "true" ]; then ExcludeFromQoS disable elif [ "$(ExcludeFromQoS check)" = "false" ]; then ExcludeFromQoS enable fi break ;; n) printf "\\n" Menu_Notifications break ;; u) printf "\\n" if Check_Lock menu; then Update_Version Clear_Lock fi PressEnter break ;; uf) printf "\\n" if Check_Lock menu; then Update_Version force Clear_Lock fi PressEnter break ;; cl) less "$SCRIPT_DIR/CHANGELOG.md" break ;; r) printf "\\n" if Check_Lock menu; then Menu_ResetDB Clear_Lock fi PressEnter break ;; e) ScriptHeader printf "\\n${BOLD}Thanks for using %s!${CLEARFORMAT}\\n\\n\\n" "$SCRIPT_NAME" exit 0 ;; z) while true; do printf "\\n${BOLD}Are you sure you want to uninstall %s? (y/n)${CLEARFORMAT} " "$SCRIPT_NAME" read -r confirm case "$confirm" in y|Y) Menu_Uninstall exit 0 ;; *) break ;; esac done ;; *) printf "\\n${BOLD}${ERR}Please choose a valid option${CLEARFORMAT}\\n\\n" ;; esac done ScriptHeader MainMenu } Check_Requirements(){ CHECKSFAILED="false" if [ "$(nvram get jffs2_scripts)" -ne 1 ]; then nvram set jffs2_scripts=1 nvram commit Print_Output true "Custom JFFS Scripts enabled" "$WARN" fi if [ ! -f /opt/bin/opkg ]; then Print_Output true "Entware not detected!" "$ERR" CHECKSFAILED="true" fi if ! Firmware_Version_Check; then Print_Output true "Unsupported firmware version detected" "$ERR" Print_Output true "$SCRIPT_NAME requires Merlin 384.15/384.13_4 or Fork 43E5 (or later)" "$ERR" CHECKSFAILED="true" fi if [ "$CHECKSFAILED" = "false" ]; then Print_Output true "Installing required packages from Entware" "$PASS" opkg update opkg install sqlite3-cli opkg install findutils opkg install bind-dig return 0 else return 1 fi } Menu_Install(){ ScriptHeader Print_Output true "Welcome to $SCRIPT_NAME $SCRIPT_VERSION, a script by JackYaz" sleep 1 Print_Output true "Checking your router meets the requirements for $SCRIPT_NAME" if ! Check_Requirements; then Print_Output true "Requirements for $SCRIPT_NAME not met, please see above for the reason(s)" "$CRIT" PressEnter Clear_Lock rm -f "/jffs/scripts/$SCRIPT_NAME" 2>/dev/null exit 1 fi Create_Dirs Conf_Exists Set_Version_Custom_Settings local "$SCRIPT_VERSION" Set_Version_Custom_Settings server "$SCRIPT_VERSION" ScriptStorageLocation load Create_Symlinks Update_File CHANGELOG.md Update_File README.md Update_File connmonstats_www.asp Update_File shared-jy.tar.gz Auto_Startup create 2>/dev/null if AutomaticMode check; then Auto_Cron create 2>/dev/null; else Auto_Cron delete 2>/dev/null; fi Auto_ServiceEvent create 2>/dev/null Shortcut_Script create echo "CREATE TABLE IF NOT EXISTS [connstats] ([StatID] INTEGER PRIMARY KEY NOT NULL,[Timestamp] NUMERIC NOT NULL,[Ping] REAL NOT NULL,[Jitter] REAL NOT NULL,[LineQuality] REAL NOT NULL,[PingTarget] TEXT NOT NULL,[PingDuration] NUMERIC);" > /tmp/connmon-stats.sql "$SQLITE3_PATH" "$SCRIPT_STORAGE_DIR/connstats.db" < /tmp/connmon-stats.sql rm -f /tmp/connmon-stats.sql touch "$SCRIPT_STORAGE_DIR/.newcolumns" touch "$SCRIPT_STORAGE_DIR/lastx.csv" Process_Upgrade Run_PingTest Clear_Lock Download_File "$SCRIPT_REPO/install-success/LICENSE" "$SCRIPT_DIR/LICENSE" ScriptHeader MainMenu } Menu_Startup(){ if [ -z "$1" ]; then Print_Output true "Missing argument for startup, not starting $SCRIPT_NAME" "$WARN" exit 1 elif [ "$1" != "force" ]; then if [ ! -f "$1/entware/bin/opkg" ]; then Print_Output true "$1 does not contain Entware, not starting $SCRIPT_NAME" "$WARN" exit 1 else Print_Output true "$1 contains Entware, starting $SCRIPT_NAME" "$WARN" fi fi NTP_Ready Check_Lock if [ "$1" != "force" ]; then sleep 6 fi Create_Dirs Conf_Exists ScriptStorageLocation load Create_Symlinks Auto_Startup create 2>/dev/null if AutomaticMode check; then Auto_Cron create 2>/dev/null; else Auto_Cron delete 2>/dev/null; fi Auto_ServiceEvent create 2>/dev/null Shortcut_Script create Mount_WebUI Clear_Lock } Menu_EditSchedule(){ exitmenu="" formattype="" crudays="" crudaysvalidated="" cruhours="" crumins="" while true; do printf "\\n${BOLD}Please choose which day(s) to run ping test\\n(0-6 - 0 = Sunday, * for every day, or comma separated days):${CLEARFORMAT} " read -r day_choice if [ "$day_choice" = "e" ]; then exitmenu="exit" break elif [ "$day_choice" = "*" ]; then crudays="$day_choice" printf "\\n" break elif [ -z "$day_choice" ]; then printf "\\n${ERR}Please enter a valid number (0-6) or comma separated values${CLEARFORMAT}\\n" else crudaystmp="$(echo "$day_choice" | sed "s/,/ /g")" crudaysvalidated="true" for i in $crudaystmp; do if echo "$i" | grep -q "-"; then if [ "$i" = "-" ]; then printf "\\n${ERR}Please enter a valid number (0-6)${CLEARFORMAT}\\n" crudaysvalidated="false" break fi crudaystmp2="$(echo "$i" | sed "s/-/ /")" for i2 in $crudaystmp2; do if ! Validate_Number "$i2"; then printf "\\n${ERR}Please enter a valid number (0-6)${CLEARFORMAT}\\n" crudaysvalidated="false" break elif [ "$i2" -lt 0 ] || [ "$i2" -gt 6 ]; then printf "\\n${ERR}Please enter a number between 0 and 6${CLEARFORMAT}\\n" crudaysvalidated="false" break fi done elif ! Validate_Number "$i"; then printf "\\n${ERR}Please enter a valid number (0-6) or comma separated values${CLEARFORMAT}\\n" crudaysvalidated="false" break elif [ "$i" -lt 0 ] || [ "$i" -gt 6 ]; then printf "\\n${ERR}Please enter a number between 0 and 6 or comma separated values${CLEARFORMAT}\\n" crudaysvalidated="false" break fi done if [ "$crudaysvalidated" = "true" ]; then crudays="$day_choice" printf "\\n" break fi fi done if [ "$exitmenu" != "exit" ]; then while true; do printf "\\n${BOLD}Please choose the format to specify the hour/minute(s)\\nto run ping test:${CLEARFORMAT}\\n" printf " 1. Every X hours/minutes\\n" printf " 2. Custom\\n\\n" printf "Choose an option: " read -r formatmenu case "$formatmenu" in 1) formattype="everyx" printf "\\n" break ;; 2) formattype="custom" printf "\\n" break ;; e) exitmenu="exit" break ;; *) printf "\\n${ERR}Please enter a valid choice (1-2)${CLEARFORMAT}\\n" ;; esac done fi if [ "$exitmenu" != "exit" ]; then if [ "$formattype" = "everyx" ]; then while true; do printf "\\n${BOLD}Please choose whether to specify every X hours or every X minutes\\nto run ping test:${CLEARFORMAT}\\n" printf " 1. Hours\\n" printf " 2. Minutes\\n\\n" printf "Choose an option: " read -r formatmenu case "$formatmenu" in 1) formattype="hours" printf "\\n" break ;; 2) formattype="mins" printf "\\n" break ;; e) exitmenu="exit" break ;; *) printf "\\n${ERR}Please enter a valid choice (1-2)${CLEARFORMAT}\\n" ;; esac done fi fi if [ "$exitmenu" != "exit" ]; then if [ "$formattype" = "hours" ]; then while true; do printf "\\n${BOLD}Please choose how often to run ping test\\n(every X hours, where X is 1-24):${CLEARFORMAT} " read -r hour_choice if [ "$hour_choice" = "e" ]; then exitmenu="exit" break elif ! Validate_Number "$hour_choice"; then printf "\\n${ERR}Please enter a valid number (1-24)${CLEARFORMAT}\\n" elif [ "$hour_choice" -lt 1 ] || [ "$hour_choice" -gt 24 ]; then printf "\\n${ERR}Please enter a number between 1 and 24${CLEARFORMAT}\\n" elif [ "$hour_choice" -eq 24 ]; then cruhours=0 crumins=0 printf "\\n" break else cruhours="*/$hour_choice" crumins=0 printf "\\n" break fi done elif [ "$formattype" = "mins" ]; then while true; do printf "\\n${BOLD}Please choose how often to run ping test\\n(every X minutes, where X is 1-30):${CLEARFORMAT} " read -r min_choice if [ "$min_choice" = "e" ]; then exitmenu="exit" break elif ! Validate_Number "$min_choice"; then printf "\\n${ERR}Please enter a valid number (1-30)${CLEARFORMAT}\\n" elif [ "$min_choice" -lt 1 ] || [ "$min_choice" -gt 30 ]; then printf "\\n${ERR}Please enter a number between 1 and 30${CLEARFORMAT}\\n" else crumins="*/$min_choice" cruhours="*" printf "\\n" break fi done fi fi if [ "$exitmenu" != "exit" ]; then if [ "$formattype" = "custom" ]; then while true; do printf "\\n${BOLD}Please choose which hour(s) to run ping test\\n(0-23, * for every hour, or comma separated hours):${CLEARFORMAT} " read -r hour_choice if [ "$hour_choice" = "e" ]; then exitmenu="exit" break elif [ "$hour_choice" = "*" ]; then cruhours="$hour_choice" printf "\\n" break else cruhourstmp="$(echo "$hour_choice" | sed "s/,/ /g")" cruhoursvalidated="true" for i in $cruhourstmp; do if echo "$i" | grep -q "-"; then if [ "$i" = "-" ]; then printf "\\n${ERR}Please enter a valid number (0-23)${CLEARFORMAT}\\n" cruhoursvalidated="false" break fi cruhourstmp2="$(echo "$i" | sed "s/-/ /")" for i2 in $cruhourstmp2; do if ! Validate_Number "$i2"; then printf "\\n${ERR}Please enter a valid number (0-23)${CLEARFORMAT}\\n" cruhoursvalidated="false" break elif [ "$i2" -lt 0 ] || [ "$i2" -gt 23 ]; then printf "\\n${ERR}Please enter a number between 0 and 23${CLEARFORMAT}\\n" cruhoursvalidated="false" break fi done elif echo "$i" | grep -q "/"; then cruhourstmp3="$(echo "$i" | sed "s/\*\///")" if ! Validate_Number "$cruhourstmp3"; then printf "\\n${ERR}Please enter a valid number (0-23)${CLEARFORMAT}\\n" cruhoursvalidated="false" break elif [ "$cruhourstmp3" -lt 0 ] || [ "$cruhourstmp3" -gt 23 ]; then printf "\\n${ERR}Please enter a number between 0 and 23${CLEARFORMAT}\\n" cruhoursvalidated="false" break fi elif ! Validate_Number "$i"; then printf "\\n${ERR}Please enter a valid number (0-23) or comma separated values${CLEARFORMAT}\\n" cruhoursvalidated="false" break elif [ "$i" -lt 0 ] || [ "$i" -gt 23 ]; then printf "\\n${ERR}Please enter a number between 0 and 23 or comma separated values${CLEARFORMAT}\\n" cruhoursvalidated="false" break fi done if [ "$cruhoursvalidated" = "true" ]; then if echo "$hour_choice" | grep -q "-"; then cruhours1="$(echo "$hour_choice" | cut -f1 -d'-')" cruhours2="$(echo "$hour_choice" | cut -f2 -d'-')" if [ "$cruhours1" -lt "$cruhours2" ]; then cruhours="$hour_choice" elif [ "$cruhours2" -lt "$cruhours1" ]; then cruhours="$cruhours1-23,0-$cruhours2" fi else cruhours="$hour_choice" fi printf "\\n" break fi fi done fi fi if [ "$exitmenu" != "exit" ]; then if [ "$formattype" = "custom" ]; then while true; do printf "\\n${BOLD}Please choose which minutes(s) to run ping test\\n(0-59, * for every minute, or comma separated minutes):${CLEARFORMAT} " read -r min_choice if [ "$min_choice" = "e" ]; then exitmenu="exit" break elif [ "$min_choice" = "*" ]; then crumins="$min_choice" printf "\\n" break else cruminstmp="$(echo "$min_choice" | sed "s/,/ /g")" cruminsvalidated="true" for i in $cruminstmp; do if echo "$i" | grep -q "-"; then if [ "$i" = "-" ]; then printf "\\n${ERR}Please enter a valid number (0-23)${CLEARFORMAT}\\n" cruminsvalidated="false" break fi cruminstmp2="$(echo "$i" | sed "s/-/ /")" for i2 in $cruminstmp2; do if ! Validate_Number "$i2"; then printf "\\n${ERR}Please enter a valid number (0-59)${CLEARFORMAT}\\n" cruminsvalidated="false" break elif [ "$i2" -lt 0 ] || [ "$i2" -gt 59 ]; then printf "\\n${ERR}Please enter a number between 0 and 59${CLEARFORMAT}\\n" cruminsvalidated="false" break fi done elif echo "$i" | grep -q "/"; then cruminstmp3="$(echo "$i" | sed "s/\*\///")" if ! Validate_Number "$cruminstmp3"; then printf "\\n${ERR}Please enter a valid number (0-30)${CLEARFORMAT}\\n" cruminsvalidated="false" break elif [ "$cruminstmp3" -lt 0 ] || [ "$cruminstmp3" -gt 30 ]; then printf "\\n${ERR}Please enter a number between 0 and 30${CLEARFORMAT}\\n" cruminsvalidated="false" break fi elif ! Validate_Number "$i"; then printf "\\n${ERR}Please enter a valid number (0-59) or comma separated values${CLEARFORMAT}\\n" cruminsvalidated="false" break elif [ "$i" -lt 0 ] || [ "$i" -gt 59 ]; then printf "\\n${ERR}Please enter a number between 0 and 59 or comma separated values${CLEARFORMAT}\\n" cruminsvalidated="false" break fi done if [ "$cruminsvalidated" = "true" ]; then if echo "$min_choice" | grep -q "-"; then crumins1="$(echo "$min_choice" | cut -f1 -d'-')" crumins2="$(echo "$min_choice" | cut -f2 -d'-')" if [ "$crumins1" -lt "$crumins2" ]; then crumins="$min_choice" elif [ "$crumins2" -lt "$crumins1" ]; then crumins="$crumins1-59,0-$crumins2" fi else crumins="$min_choice" fi printf "\\n" break fi fi done fi fi if [ "$exitmenu" != "exit" ]; then TestSchedule update "$crudays" "$cruhours" "$crumins" return 0 else return 1 fi } Menu_ResetDB(){ printf "${BOLD}\\e[33mWARNING: This will reset the %s database by deleting all database records.\\n" "$SCRIPT_NAME" printf "A backup of the database will be created if you change your mind.${CLEARFORMAT}\\n" printf "\\n${BOLD}Do you want to continue? (y/n)${CLEARFORMAT} " read -r confirm case "$confirm" in y|Y) printf "\\n" Reset_DB ;; *) printf "\\n${BOLD}\\e[33mDatabase reset cancelled${CLEARFORMAT}\\n\\n" ;; esac } Menu_Uninstall(){ Print_Output true "Removing $SCRIPT_NAME..." "$PASS" if [ -n "$PPID" ]; then ps | grep -v grep | grep -v $$ | grep -v "$PPID" | grep -i "$SCRIPT_NAME" | grep generate | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 else ps | grep -v grep | grep -v $$ | grep -i "$SCRIPT_NAME" | grep generate | awk '{print $1}' | xargs kill -9 >/dev/null 2>&1 fi Auto_Startup delete 2>/dev/null Auto_Cron delete 2>/dev/null Auto_ServiceEvent delete 2>/dev/null Shortcut_Script delete LOCKFILE=/tmp/addonwebui.lock FD=386 eval exec "$FD>$LOCKFILE" flock -x "$FD" Get_WebUI_Page "$SCRIPT_DIR/connmonstats_www.asp" if [ -n "$MyPage" ] && [ "$MyPage" != "none" ] && [ -f /tmp/menuTree.js ]; then sed -i "\\~$MyPage~d" /tmp/menuTree.js umount /www/require/modules/menuTree.js mount -o bind /tmp/menuTree.js /www/require/modules/menuTree.js rm -f "$SCRIPT_WEBPAGE_DIR/$MyPage" rm -f "$SCRIPT_WEBPAGE_DIR/$(echo $MyPage | cut -f1 -d'.').title" fi flock -u "$FD" rm -f "$SCRIPT_DIR/connmonstats_www.asp" 2>/dev/null printf "\\n${BOLD}Do you want to delete %s config and stats? (y/n)${CLEARFORMAT} " "$SCRIPT_NAME" read -r confirm case "$confirm" in y|Y) rm -rf "$SCRIPT_DIR" 2>/dev/null rm -rf "$SCRIPT_STORAGE_DIR" 2>/dev/null ;; *) : ;; esac SETTINGSFILE="/jffs/addons/custom_settings.txt" sed -i '/connmon_version_local/d' "$SETTINGSFILE" sed -i '/connmon_version_server/d' "$SETTINGSFILE" rm -rf "$SCRIPT_WEB_DIR" 2>/dev/null rm -f "/jffs/scripts/$SCRIPT_NAME" 2>/dev/null Clear_Lock Print_Output true "Uninstall completed" "$PASS" } NTP_Ready(){ if [ "$(nvram get ntp_ready)" -eq 0 ]; then ntpwaitcount=0 Check_Lock while [ "$(nvram get ntp_ready)" -eq 0 ] && [ "$ntpwaitcount" -lt 600 ]; do ntpwaitcount="$((ntpwaitcount + 30))" Print_Output true "Waiting for NTP to sync..." "$WARN" sleep 30 done if [ "$ntpwaitcount" -ge 600 ]; then Print_Output true "NTP failed to sync after 10 minutes. Please resolve!" "$CRIT" Clear_Lock exit 1 else Print_Output true "NTP synced, $SCRIPT_NAME will now continue" "$PASS" Clear_Lock fi fi } ### function based on @Adamm00's Skynet USB wait function ### Entware_Ready(){ if [ ! -f /opt/bin/opkg ]; then Check_Lock sleepcount=1 while [ ! -f /opt/bin/opkg ] && [ "$sleepcount" -le 10 ]; do Print_Output true "Entware not found, sleeping for 10s (attempt $sleepcount of 10)" "$ERR" sleepcount="$((sleepcount + 1))" sleep 10 done if [ ! -f /opt/bin/opkg ]; then Print_Output true "Entware not found and is required for $SCRIPT_NAME to run, please resolve" "$CRIT" Clear_Lock exit 1 else Print_Output true "Entware found, $SCRIPT_NAME will now continue" "$PASS" Clear_Lock fi fi } ### ### ### function based on @dave14305's FlexQoS about function ### Show_About(){ cat </dev/null if AutomaticMode check; then Auto_Cron create 2>/dev/null; else Auto_Cron delete 2>/dev/null; fi Auto_ServiceEvent create 2>/dev/null Shortcut_Script create Process_Upgrade ScriptHeader MainMenu exit 0 fi case "$1" in install) Check_Lock Menu_Install exit 0 ;; startup) Menu_Startup "$2" exit 0 ;; generate) NTP_Ready Entware_Ready Check_Lock Run_PingTest Clear_Lock exit 0 ;; outputcsv) NTP_Ready Entware_Ready Check_Lock Generate_CSVs Clear_Lock exit 0 ;; enable) Entware_Ready AutomaticMode enable exit 0 ;; disable) Entware_Ready AutomaticMode disable exit 0 ;; service_event) if [ "$2" = "start" ] && [ "$3" = "$SCRIPT_NAME" ]; then rm -f "$SCRIPT_WEB_DIR/detect_connmon.js" rm -f /tmp/pingfile.txt rm -f "$SCRIPT_WEB_DIR/ping-result.txt" Check_Lock webui sleep 3 Run_PingTest Clear_Lock exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}config" ]; then echo 'var savestatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_save.js" sleep 3 Conf_FromSettings echo 'var savestatus = "Success";' > "$SCRIPT_WEB_DIR/detect_save.js" exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}emailconfig" ]; then echo 'var savestatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_save.js" sleep 3 EmailConf_FromSettings echo 'var savestatus = "Success";' > "$SCRIPT_WEB_DIR/detect_save.js" exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}checkupdate" ]; then Update_Check exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}doupdate" ]; then Update_Version force unattended exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}emailpassword" ]; then if Email_ConfExists; then rm -f "$SCRIPT_WEB_DIR/password.htm" sleep 3 Email_Decrypt_Password > "$SCRIPT_WEB_DIR/password.htm" fi elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}deleteemailpassword" ]; then rm -f "$SCRIPT_WEB_DIR/password.htm" elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}customactionlist" ]; then rm -f "$SCRIPT_STORAGE_DIR/.customactionlist" sleep 3 CustomAction_List silent exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}TestEmail" ]; then rm -f "$SCRIPT_WEB_DIR/detect_test.js" echo 'var teststatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_test.js" NOTIFICATIONS_EMAIL_LIST="$(Email_Recipients check)" if [ -z "$NOTIFICATIONS_EMAIL_LIST" ]; then if SendEmail "Test email - $(/bin/date +"%c")" "This is a test email!"; then echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" else echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi else IFS=$',' success="true" for EMAIL in $NOTIFICATIONS_EMAIL_LIST; do if ! SendEmail "Test email - $(/bin/date +"%c")" "This is a test email!" "$EMAIL"; then success="false" fi done if [ "$success" = "true" ]; then echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" else echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi fi unset IFS exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}TestWebhooks" ]; then rm -f "$SCRIPT_WEB_DIR/detect_test.js" echo 'var teststatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_test.js" NOTIFICATIONS_WEBHOOK_LIST="$(Webhook_Targets check)" if [ -z "$NOTIFICATIONS_WEBHOOK_LIST" ]; then echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi IFS=$',' success="true" for WEBHOOK in $NOTIFICATIONS_WEBHOOK_LIST; do if ! SendWebhook "$(/bin/date +"%c")\n\nThis is a test webhook message!" "$WEBHOOK"; then success="false" fi done unset IFS if [ "$success" = "true" ]; then echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" else echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}TestPushover" ]; then rm -f "$SCRIPT_WEB_DIR/detect_test.js" echo 'var teststatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_test.js" if SendPushover "$(/bin/date +"%c")"$'\n'$'\n'"This is a test pushover message!"; then echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" else echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}TestCustomActions" ]; then rm -f "$SCRIPT_WEB_DIR/detect_test.js" echo 'var teststatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_test.js" if [ -z "$(ls -A "$USER_SCRIPT_DIR")" ]; then echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" else printf "\\n" FILES="$USER_SCRIPT_DIR/*.sh" for f in $FILES; do if [ -f "$f" ]; then sh "$f" "$(/bin/date +%c)" "30 ms" "15 ms" "90%" fi done echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" fi exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}TestHealthcheck" ]; then rm -f "$SCRIPT_WEB_DIR/detect_test.js" echo 'var teststatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_test.js" if SendHealthcheckPing "Pass"; then echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" else echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi exit 0 elif [ "$2" = "start" ] && [ "$3" = "${SCRIPT_NAME}TestInfluxDB" ]; then rm -f "$SCRIPT_WEB_DIR/detect_test.js" echo 'var teststatus = "InProgress";' > "$SCRIPT_WEB_DIR/detect_test.js" if SendToInfluxDB "$(/bin/date +%s)" 30 15 90; then echo 'var teststatus = "Success";' > "$SCRIPT_WEB_DIR/detect_test.js" else echo 'var teststatus = "Fail";' > "$SCRIPT_WEB_DIR/detect_test.js" fi exit 0 fi exit 0 ;; update) Update_Version exit 0 ;; forceupdate) Update_Version force exit 0 ;; postupdate) Create_Dirs Conf_Exists ScriptStorageLocation load Create_Symlinks Auto_Startup create 2>/dev/null if AutomaticMode check; then Auto_Cron create 2>/dev/null; else Auto_Cron delete 2>/dev/null; fi Auto_ServiceEvent create 2>/dev/null Shortcut_Script create Process_Upgrade exit 0 ;; uninstall) Menu_Uninstall exit 0 ;; develop) SCRIPT_BRANCH="develop" SCRIPT_REPO="https://jackyaz.io/$SCRIPT_NAME/$SCRIPT_BRANCH" Update_Version force exit 0 ;; stable) SCRIPT_BRANCH="master" SCRIPT_REPO="https://jackyaz.io/$SCRIPT_NAME/$SCRIPT_BRANCH" Update_Version force exit 0 ;; about) ScriptHeader Show_About exit 0 ;; help) ScriptHeader Show_Help exit 0 ;; *) ScriptHeader Print_Output false "Command not recognised" "$ERR" Print_Output false "For a list of available commands run: $SCRIPT_NAME help" exit 1 ;; esac