From 1d90509b0351458f66c68d8106f7a8e9bfdd904d Mon Sep 17 00:00:00 2001 From: Dirk Brenken Date: Thu, 29 Aug 2019 14:03:23 +0200 Subject: [PATCH] travelmate: update 1.4.12 * automatically add open uplinks to your wireless config, e.g. hotel captive portals (disabled by default) * shift net status check in a separate function * (s)hellcheck cosmetics Signed-off-by: Dirk Brenken --- net/travelmate/Makefile | 2 +- net/travelmate/files/README.md | 6 +- net/travelmate/files/travelmate.conf | 1 + net/travelmate/files/travelmate.sh | 139 ++++++++++++++++----------- 4 files changed, 89 insertions(+), 59 deletions(-) diff --git a/net/travelmate/Makefile b/net/travelmate/Makefile index 17eb903b8..2d961144d 100644 --- a/net/travelmate/Makefile +++ b/net/travelmate/Makefile @@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk PKG_NAME:=travelmate -PKG_VERSION:=1.4.11 +PKG_VERSION:=1.4.12 PKG_RELEASE:=1 PKG_LICENSE:=GPL-3.0+ PKG_MAINTAINER:=Dirk Brenken diff --git a/net/travelmate/files/README.md b/net/travelmate/files/README.md index f84fffa73..4f7eab676 100644 --- a/net/travelmate/files/README.md +++ b/net/travelmate/files/README.md @@ -12,6 +12,7 @@ To avoid these kind of deadlocks, travelmate will set all station interfaces to * fast uplink connections * support all kinds of uplinks, incl. hidden and enterprise uplinks * continuously checks the existing uplink connection (quality), e.g. for conditional uplink (dis-) connections +* automatically add open uplinks to your wireless config, e.g. hotel captive portals * captive portal detection with internet online check and a 'heartbeat' function to keep the uplink connection up & running * captive portal auto-login hook (configured via uci/LuCI), you could reference an external script for captive portal auto-logins (see example below) * proactively scan and switch to a higher prioritized uplink, despite of an already existing connection @@ -47,6 +48,7 @@ To avoid these kind of deadlocks, travelmate will set all station interfaces to * trm\_debug => enable/disable debug logging (bool/default: '0', disabled) * trm\_captive => enable/disable the captive portal detection (bool/default: '1', enabled) * trm\_proactive => enable/disable the proactive uplink switch (bool/default: '1', enabled) + * trm\_autoadd => automatically add open uplinks to your wireless config (bool/default: '0', disabled) * trm\_minquality => minimum signal quality threshold as percent for conditional uplink (dis-) connections (int/default: '35', valid range: 20-80) * trm\_maxwait => how long (in seconds) should travelmate wait for a successful wlan interface reload action (int/default: '30', valid range: 20-40) * trm\_maxretry => how many times should travelmate try to connect to an uplink (int/default: '3', valid range: 1-10) @@ -56,9 +58,9 @@ To avoid these kind of deadlocks, travelmate will set all station interfaces to * trm\_triggerdelay => additional trigger delay in seconds before travelmate processing begins (int/default: '2') ## Captive Portal auto-logins -For automated captive portal logins you could reference external shell scripts. All login scripts should be executable and located in '/etc/travelmate' with the extension '.login'. The provided 'wifionice.login' script example requires curl and automates the login to german ICE hotspots, it also explains the principle approach to extract runtime data like security tokens for a succesful login. Hopefully more scripts for different captive portals will be provided by the community ... +For automated captive portal logins you could reference external shell scripts. All login scripts should be executable and located in '/etc/travelmate' with the extension '.login'. The provided 'wifionice.login' script example requires curl and automates the login to german ICE hotspots, it also explains the principle approach to extract runtime data like security tokens for a successful login. Hopefully more scripts for different captive portals will be provided by the community ... -A typical/succesful captive portal login looks like this: +A typical/successful captive portal login looks like this:

 [...]
 Mon Aug  5 10:15:48 2019 user.info travelmate-1.4.10[1481]: travelmate instance started ::: action: start, pid: 1481
diff --git a/net/travelmate/files/travelmate.conf b/net/travelmate/files/travelmate.conf
index 1833b92a7..ce9eb5f97 100644
--- a/net/travelmate/files/travelmate.conf
+++ b/net/travelmate/files/travelmate.conf
@@ -6,6 +6,7 @@ config travelmate 'global'
 	option trm_captive '1'
 	option trm_proactive '1'
 	option trm_netcheck '0'
+	option trm_autoadd '0'
 	option trm_iface 'trm_wwan'
 	option trm_triggerdelay '2'
 	option trm_debug '0'
diff --git a/net/travelmate/files/travelmate.sh b/net/travelmate/files/travelmate.sh
index 1e00179a2..8cd1f3841 100755
--- a/net/travelmate/files/travelmate.sh
+++ b/net/travelmate/files/travelmate.sh
@@ -6,18 +6,21 @@
 # You should have received a copy of the GNU General Public License
 # along with this program. If not, see .
 
+# (s)hellcheck exceptions
+# shellcheck disable=1091 disable=2039 disable=2143 disable=2181 disable=2188
+
 # set initial defaults
 #
 LC_ALL=C
 PATH="/usr/sbin:/usr/bin:/sbin:/bin"
-trm_ver="1.4.11"
-trm_sysver="unknown"
+trm_ver="1.4.12"
 trm_enabled=0
 trm_debug=0
 trm_iface="trm_wwan"
 trm_captive=1
 trm_proactive=1
 trm_netcheck=0
+trm_autoadd=0
 trm_captiveurl="http://captive.apple.com"
 trm_scanbuffer=1024
 trm_minquality=35
@@ -49,7 +52,7 @@ f_trim()
 #
 f_envload()
 {
-	local IFS sys_call sys_desc sys_model
+	local IFS
 
 	# (re-)initialize global list variables
 	#
@@ -57,17 +60,12 @@ f_envload()
 
 	# get system information
 	#
-	sys_call="$(ubus -S call system board 2>/dev/null)"
-	if [ -n "${sys_call}" ]
-	then
-		sys_desc="$(printf '%s' "${sys_call}" | jsonfilter -e '@.release.description')"
-		sys_model="$(printf '%s' "${sys_call}" | jsonfilter -e '@.model')"
-		trm_sysver="${sys_model}, ${sys_desc}"
-	fi
+	trm_sysver="$(ubus -S call system board 2>/dev/null | jsonfilter -e '@.model' -e '@.release.description' | \
+		awk 'BEGIN{ORS=", "}{print $0}' | awk '{print substr($0,1,length($0)-2)}')"
 
 	# get eap capabilities
 	#
-	trm_eap="$("${trm_wpa}" -veap >/dev/null 2>&1; printf "%u" ${?})"
+	trm_eap="$("${trm_wpa}" -veap >/dev/null 2>&1; printf "%u" "${?}")"
 
 	# load config and check 'enabled' option
 	#
@@ -137,11 +135,11 @@ f_prep()
 {
 	local IFS mode network radio disabled eaptype config="${1}" proactive="${2}"
 
-	mode="$(uci_get wireless "${config}" mode)"
-	network="$(uci_get wireless "${config}" network)"
-	radio="$(uci_get wireless "${config}" device)"
-	disabled="$(uci_get wireless "${config}" disabled)"
-	eaptype="$(uci_get wireless "${config}" eap_type)"
+	mode="$(uci_get "wireless" "${config}" "mode")"
+	network="$(uci_get "wireless" "${config}" "network")"
+	radio="$(uci_get "wireless" "${config}" "device")"
+	disabled="$(uci_get "wireless" "${config}" "disabled")"
+	eaptype="$(uci_get "wireless" "${config}" "eap_type")"
 
 	if [ -n "${config}" ] && [ -n "${radio}" ] && [ -n "${mode}" ] && [ -n "${network}" ]
 	then
@@ -171,17 +169,29 @@ f_prep()
 	f_log "debug" "f_prep ::: config: ${config}, mode: ${mode}, network: ${network}, radio: ${radio}, trm_radio: ${trm_radio:-"-"}, trm_active_sta: ${trm_active_sta:-"-"}, proactive: ${proactive}, trm_eap: ${trm_eap:-"-"}, disabled: ${disabled}"
 }
 
+# check net status
+#
+f_net()
+{
+	local IFS result
+
+	result="$(${trm_fetch} --timeout=$((trm_maxwait/6)) "${trm_captiveurl}" -O /dev/null 2>&1 | \
+		awk '/^Failed to redirect|^Redirected/{printf "%s" "net cp \047"$NF"\047";exit}/^Download completed/{printf "%s" "net ok";exit}/^Failed|Connection error/{printf "%s" "net nok";exit}')"
+	printf "%s" "${result}"
+	f_log "debug" "f_net  ::: fetch: ${trm_fetch}, timeout: $((trm_maxwait/6)), url: ${trm_captiveurl}, result: ${result}"
+}
+
 # check interface status
 #
 f_check()
 {
-	local IFS ifname radio dev_status config sta_essid sta_bssid result uci_essid uci_bssid login_command bg_pid wait_time mode="${1}" status="${2:-"false"}" cp_domain="${3:-"false"}"
+	local IFS ifname radio dev_status config sta_essid sta_bssid result uci_essid uci_bssid login_command wait_time mode="${1}" status="${2:-"false"}" cp_domain="${3:-"false"}"
 
 	if [ "${mode}" != "initial" ] && [ "${status}" = "false" ]
 	then
 		ubus call network reload
 		wait_time=$((trm_maxwait/6))
-		sleep ${wait_time}
+		sleep "${wait_time}"
 	fi
 
 	wait_time=1
@@ -222,13 +232,12 @@ f_check()
 					trm_ifquality="$(${trm_iwinfo} "${ifname}" info 2>/dev/null | awk -F "[ ]" '/Link Quality:/{split($NF,var0,"/");printf "%i\n",(var0[1]*100/var0[2])}')"
 					if [ "${mode}" = "initial" ] && [ "${trm_captive}" -eq 1 ]
 					then
-						result="$(${trm_fetch} --timeout=$((trm_maxwait/6)) "${trm_captiveurl}" -O /dev/null 2>&1 | \
-							awk '/^Failed to redirect|^Redirected/{printf "%s" "net cp \047"$NF"\047";exit}/^Download completed/{printf "%s" "net ok";exit}/^Failed|Connection error/{printf "%s" "net nok";exit}')"
+						result="$(f_net)"
 						if [ "${cp_domain}" = "true" ]
 						then
 							cp_domain="$(printf "%s" "${result}" | awk -F "[\\'| ]" '/^net cp/{printf "%s" $4}')"
 							uci_essid="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].config.ssid')"
-							uci_essid="$(printf "%s" "${uci_essid//[^[:alnum:]_]/_}" | awk '{print tolower($1)}')"
+							uci_essid="${uci_essid//[^[:alnum:]_]/_}"
 							uci_bssid="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].config.bssid')"
 							uci_bssid="${uci_bssid//[^[:alnum:]_]/_}"
 						fi
@@ -242,11 +251,10 @@ f_check()
 							then
 								while true
 								do
-									result="$(${trm_fetch} --timeout=$((trm_maxwait/6)) "${trm_captiveurl}" -O /dev/null 2>&1 | \
-										awk '/^Failed to redirect|^Redirected/{printf "%s" "net cp \047"$NF"\047";exit}/^Download completed/{printf "%s" "net ok";exit}/^Failed|Connection error/{printf "%s" "net nok";exit}')"
+									result="$(f_net)"
 									cp_domain="$(printf "%s" "${result}" | awk -F "[\\'| ]" '/^net cp/{printf "%s" $4}')"
 									uci_essid="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].config.ssid')"
-									uci_essid="$(printf "%s" "${uci_essid//[^[:alnum:]_]/_}" | awk '{print tolower($1)}')"
+									uci_essid="${uci_essid//[^[:alnum:]_]/_}"
 									uci_bssid="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].config.bssid')"
 									uci_bssid="${uci_bssid//[^[:alnum:]_]/_}"
 									if [ "${trm_netcheck}" -eq 1 ] && [ "${result}" = "net nok" ]
@@ -255,34 +263,34 @@ f_check()
 										f_jsnup
 										break 2
 									fi
-									if [ -z "${cp_domain}" ] || [ -n "$(uci_get dhcp "@dnsmasq[0]" rebind_domain | grep -Fo "${cp_domain}")" ]
+									if [ -z "${cp_domain}" ] || [ -n "$(uci_get "dhcp" "@dnsmasq[0]" "rebind_domain" | grep -Fo "${cp_domain}")" ]
 									then
 										break
 									fi
 									uci -q add_list dhcp.@dnsmasq[0].rebind_domain="${cp_domain}"
 									f_log "info" "captive portal domain '${cp_domain}' added to to dhcp rebind whitelist"
-									if [ -z "$(uci_get travelmate "${uci_essid}${uci_bssid}")" ]
+									if [ -z "$(uci_get "travelmate" "${uci_essid}${uci_bssid}")" ]
 									then
 										uci_add travelmate "login" "${uci_essid}${uci_bssid}"
 										uci_set travelmate "${uci_essid}${uci_bssid}" "command" "none"
 										f_log "info" "captive portal login section '${uci_essid}${uci_bssid}' added to travelmate config section"
 									fi
 								done
-								if [ -n "$(uci -q changes dhcp)" ]
+								if [ -n "$(uci -q changes "dhcp")" ]
 								then
-									uci_commit dhcp
+									uci_commit "dhcp"
 									/etc/init.d/dnsmasq reload
 								fi
-								if [ -n "$(uci -q changes travelmate)" ]
+								if [ -n "$(uci -q changes "travelmate")" ]
 								then
-									uci_commit travelmate
+									uci_commit "travelmate"
 								fi
 							fi
 							if [ -n "${cp_domain}" ] && [ "${cp_domain}" != "false" ] && [ -n "${uci_essid}" ] && [ "${trm_captive}" -eq 1 ]
 							then
 								trm_connection="${result:-"-"}/${trm_ifquality}"
 								f_jsnup
-								login_command="$(uci_get travelmate "${uci_essid}${uci_bssid}" command)"
+								login_command="$(uci_get "travelmate" "${uci_essid}${uci_bssid}" "command")"
 								if [ -x "${login_command}" ]
 								then
 									"${login_command}" >/dev/null 2>&1
@@ -290,8 +298,7 @@ f_check()
 									f_log "info" "captive portal login '${login_command:0:40}' for '${cp_domain}' has been executed with rc '${rc}'"
 									if [ "${rc}" -eq 0 ]
 									then
-										result="$(${trm_fetch} --timeout=$((trm_maxwait/6)) "${trm_captiveurl}" -O /dev/null 2>&1 | \
-											awk '/^Failed to redirect|^Redirected/{printf "%s" "net cp \047"$NF"\047";exit}/^Download completed/{printf "%s" "net ok";exit}/^Failed|Connection error/{printf "%s" "net nok";exit}')"
+										result="$(f_net)"
 									fi
 								fi
 							fi
@@ -352,10 +359,10 @@ f_jsnup()
 		config="$(printf "%s" "${dev_status}" | jsonfilter -l1 -e '@.*.interfaces[@.config.mode="sta"].section')"
 		if [ -n "${config}" ]
 		then
-			sta_iface="$(uci_get wireless "${config}" network)"
-			sta_radio="$(uci_get wireless "${config}" device)"
-			sta_essid="$(uci_get wireless "${config}" ssid)"
-			sta_bssid="$(uci_get wireless "${config}" bssid)"
+			sta_iface="$(uci_get "wireless" "${config}" "network")"
+			sta_radio="$(uci_get "wireless" "${config}" "device")"
+			sta_essid="$(uci_get "wireless" "${config}" "ssid")"
+			sta_bssid="$(uci_get "wireless" "${config}" "bssid")"
 		fi
 	fi
 
@@ -436,7 +443,7 @@ f_log()
 #
 f_main()
 {
-	local IFS cnt dev config spec scan_list scan_essid scan_bssid scan_quality faulty_list
+	local IFS cnt dev config spec scan_list scan_essid scan_bssid scan_open scan_quality uci_essid cfg_essid faulty_list
 	local station_id sta sta_essid sta_bssid sta_radio sta_iface active_essid active_bssid active_radio
 
 	f_check "initial" "false" "true"
@@ -455,7 +462,7 @@ f_main()
 			f_check "dev" "true"
 			f_log "debug" "f_main ::: active_radio: ${active_radio}, active_essid: \"${active_essid}\", active_bssid: ${active_bssid:-"-"}"
 		else
-			uci_commit wireless
+			uci_commit "wireless"
 			f_check "dev"
 		fi
 		json_get_var faulty_list "faulty_stations"
@@ -475,9 +482,9 @@ f_main()
 			do
 				config="${sta%%-*}"
 				sta_radio="${sta##*-}"
-				sta_essid="$(uci_get wireless "${config}" ssid)"
-				sta_bssid="$(uci_get wireless "${config}" bssid)"
-				sta_iface="$(uci_get wireless "${config}" network)"
+				sta_essid="$(uci_get "wireless" "${config}" "ssid")"
+				sta_bssid="$(uci_get "wireless" "${config}" "bssid")"
+				sta_iface="$(uci_get "wireless" "${config}" "network")"
 				json_get_var faulty_list "faulty_stations"
 				if [ -n "$(printf "%s" "${faulty_list}" | grep -Fo "${sta_radio}/${sta_essid}/${sta_bssid}")" ]
 				then
@@ -493,8 +500,8 @@ f_main()
 				if [ -z "${scan_list}" ]
 				then
 					scan_list="$("${trm_iwinfo}" "${dev}" scan 2>/dev/null | \
-						awk 'BEGIN{FS="[ ]"}/Address:/{var1=$NF}/ESSID:/{var2="";for(i=12;i<=NF;i++)if(var2==""){var2=$i}else{var2=var2" "$i};
-						gsub(/,/,".",var2)}/Quality:/{split($NF,var0,"/");printf "%i,%s,%s\n",(var0[1]*100/var0[2]),var1,var2}' | \
+						awk 'BEGIN{FS="[[:space:]]"}/Address:/{var1=$NF}/ESSID:/{var2="";for(i=12;i<=NF;i++)if(var2==""){var2=$i}else{var2=var2" "$i};
+						gsub(/,/,".",var2)}/Quality:/{split($NF,var0,"/")}/Encryption:/{if($NF=="none"){var3="+"}else{var3="-"};printf "%i,%s,%s,%s\n",(var0[1]*100/var0[2]),var1,var2,var3}' | \
 						sort -rn | awk -v buf="${trm_scanbuffer}" 'BEGIN{ORS=","}{print substr($0,1,buf)}')"
 					f_log "debug" "f_main ::: scan_buffer: ${trm_scanbuffer}, scan_list: ${scan_list}"
 					if [ -z "${scan_list}" ]
@@ -517,36 +524,40 @@ f_main()
 					elif [ -z "${scan_essid}" ]
 					then
 						scan_essid="${spec}"
+					elif [ -z "${scan_open}" ]
+					then
+						scan_open="${spec}"
 					fi
-					if [ -n "${scan_quality}" ] && [ -n "${scan_bssid}" ] && [ -n "${scan_essid}" ]
+					if [ -n "${scan_quality}" ] && [ -n "${scan_bssid}" ] && [ -n "${scan_essid}" ] && [ -n "${scan_open}" ]
 					then
 						if [ "${scan_quality}" -ge "${trm_minquality}" ]
 						then
 							if { { [ "${scan_essid}" = "\"${sta_essid//,/.}\"" ] && { [ -z "${sta_bssid}" ] || [ "${scan_bssid}" = "${sta_bssid}" ]; } } || \
 								{ [ "${scan_bssid}" = "${sta_bssid}" ] && [ "${scan_essid}" = "unknown" ]; } } && [ "${dev}" = "${sta_radio}" ]
 							then
-								f_log "debug" "f_main ::: scan_quality: ${scan_quality}, scan_essid: ${scan_essid}, scan_bssid: ${scan_bssid:-"-"}"
+								f_log "debug" "f_main ::: scan_quality: ${scan_quality}, scan_essid: ${scan_essid}, scan_bssid: ${scan_bssid:-"-"}, scan_open: ${scan_open}"
 								if [ "${dev}" = "${active_radio}" ]
 								then
+									uci_set "wireless" "${trm_active_sta}" "disabled" "1"
+									uci_commit "wireless"
+									f_log "debug" "f_main ::: active uplink connection '${active_radio}/${active_essid}/${active_bssid:-"-"}' terminated"
 									unset trm_connection active_radio active_essid active_bssid
-									uci_set wireless "${trm_active_sta}" disabled 1
-									uci_commit wireless
 								fi
 								# retry loop
 								#
 								cnt=1
 								while [ "${cnt}" -le "${trm_maxretry}" ]
 								do
-									uci_set wireless "${config}" disabled 0
+									uci_set "wireless" "${config}" "disabled" "0"
 									f_check "sta"
 									if [ "${trm_ifstatus}" = "true" ]
 									then
 										unset IFS scan_list
-										uci_commit wireless
+										uci_commit "wireless"
 										f_log "info" "connected to uplink '${sta_radio}/${sta_essid}/${sta_bssid:-"-"}' (${cnt}/${trm_maxretry}, ${trm_sysver})"
 										return 0
 									else
-										uci -q revert wireless
+										uci -q revert "wireless"
 										f_check "rev"
 										if [ "${cnt}" -eq "${trm_maxretry}" ]
 										then
@@ -562,17 +573,33 @@ f_main()
 									cnt=$((cnt+1))
 									sleep $((trm_maxwait/6))
 								done
-							else
-								unset scan_quality scan_bssid scan_essid
-								continue
+							elif [ "${trm_autoadd}" -eq 1 ] && [ "${scan_open}" = "+" ] && [ "${scan_essid}" != "unknown" ]
+							then
+								cfg_essid="${scan_essid#*\"}"
+								cfg_essid="${cfg_essid%\"*}"
+								uci_essid="${cfg_essid//[^[:alnum:]_]/_}"
+								if [ -z "$(uci_get "wireless" "trm_${uci_essid}")" ]
+								then
+									uci_add "wireless" "wifi-iface" "trm_${uci_essid}"
+									uci_set "wireless" "trm_${uci_essid}" "mode" "sta"
+									uci_set "wireless" "trm_${uci_essid}" "network" "${trm_iface}"
+									uci_set "wireless" "trm_${uci_essid}" "device" "${sta_radio}"
+									uci_set "wireless" "trm_${uci_essid}" "ssid" "${cfg_essid}"
+									uci_set "wireless" "trm_${uci_essid}" "encryption" "none"
+									uci_set "wireless" "trm_${uci_essid}" "disabled" "1"
+									uci_commit "wireless"
+									f_log "info" "open uplink '${sta_radio}/${cfg_essid}' added to wireless config"
+								fi
 							fi
+							unset scan_quality scan_bssid scan_essid scan_open
+							continue
 						else
-							unset scan_quality scan_bssid scan_essid
+							unset scan_quality scan_bssid scan_essid scan_open
 							continue
 						fi
 					fi
 				done
-				unset IFS scan_quality scan_bssid scan_essid
+				unset IFS scan_quality scan_bssid scan_essid scan_open
 			done
 			unset scan_list
 		done