| #!/bin/bash |
| |
| # This script stops shill, associates with each test AP in turn, |
| # and harvests signal strength and quality numbers for us, then restarts |
| # shill so the old network can be reacquired and our test can complete |
| |
| set +o posix; |
| shopt -s extglob; |
| |
| oldstderr=; |
| stderrlog=; |
| |
| output_to_vtx () { |
| # copy stderr to the next free vt so the user can see progress |
| # on the DUT, otherwise they're just sitting there with no feedback |
| # (if we can, that is - if openvt isn't there, just get on with it) |
| if which openvt; |
| then |
| stderrlog=/tmp/$RANDOM.vtx.log; |
| exec {oldstderr}>&2; |
| exec 2>$stderrlog; |
| tail --pid $$ -f $stderrlog >&$oldstderr & |
| openvt -s -w -- tail --pid $$ -f $stderrlog & |
| fi |
| } |
| |
| close_vtx () { |
| if [[ -f "$stderrlog" ]]; then |
| rm "$stderrlog"; |
| fi; |
| } |
| |
| progress () { echo "$@" 1>&2; } |
| |
| contains_modulations () { |
| # check that at least one modulation in `wanted' is present in `supported' |
| supported=$1; |
| wanted=$2; |
| |
| case $supported in |
| *[$wanted]*) |
| return 0; |
| ;; |
| esac |
| |
| return 1; |
| } |
| |
| # pick a WiFi interface to test |
| find_wifi_if () { |
| iface="$1"; |
| |
| if [[ -z "$iface" ]]; then |
| while read _iface _ignore && test -z "$iface"; do |
| iface=$_iface; |
| done < <(iwconfig 2>/dev/null | grep '^[a-z]'); |
| fi; |
| |
| test -n "$iface"; |
| } |
| |
| wifi_status () { |
| # harvest the state of the target interface: modulation, essid and so forth: |
| find_wifi_if "$1"; |
| |
| if_80211=; |
| if_essid=; |
| if_mode=; |
| if_ap=; |
| if_rate=; |
| if_txp=; |
| if_quality=; |
| if_signal=; |
| if_freq=; |
| |
| # iwconfig's output is a pain to parse, but is stable. |
| # the newer tools are much easier to parse, but they are |
| # considered unstable by the authors, who specifically forbid |
| # scraping their output until the specification stabilises. |
| while read data; do |
| case "$data" in |
| $iface*) |
| if_essid=${data##*ESSID:*(\")}; |
| if_essid=${if_essid%\"*}; |
| if_80211=${data%%+( )ESSID:*}; |
| if_80211=${if_80211#*802.11}; |
| ;; |
| Mode:*) |
| if_mode=${data#Mode:} |
| if_mode=${if_mode%% *}; |
| if_ap=${data##*Access Point: }; |
| if_ap=${if_ap%% *}; |
| if_freq=${data##*Frequency:}; |
| if_freq=${if_freq%%+( )Access Point:*}; |
| if [[ "$if_ap" = "Not-Associated" ]]; then |
| if_txp=${data##*Tx-Power=}; |
| fi |
| ;; |
| Bit\ Rate*) |
| if_rate=${data%%+( )Tx-*}; |
| if_rate=${if_rate#Bit Rate=}; |
| if [[ -z $"if_txp" ]]; then |
| if_txp=${data##*Tx-Power=}; |
| fi; |
| ;; |
| Link*) |
| if_quality=${data%%+( )Signal*}; |
| if_quality=${if_quality#Link Quality=}; |
| if_signal=${data##*Signal level=}; |
| ;; |
| esac; |
| done < <(iwconfig $iface) |
| } |
| |
| wifi_scan () { |
| iface=$1; |
| |
| # Trigger a wifi scan. The DUT doesn't necessarily scan all frequencies |
| # or remember APs on frequencies it isn't currently on, so we need to do |
| # this at times to make sure we can interact with the test AP: |
| progress Bringing up $iface; |
| ifconfig $iface up 1>&2; |
| progress Scanning for CrOS test ESSIDs; |
| |
| lofreq_aps=""; |
| hifreq_aps=""; |
| |
| cell_freq=; |
| cell_essid=; |
| |
| while read scan; do |
| if [[ -z "$cell_freq" || -z "$cell_essid" ]]; then |
| case "$scan" in |
| Frequency:*) |
| cell_freq=${scan##*Frequency:} |
| cell_freq=${cell_freq%% *}; |
| cell_freq=${cell_freq:-0}; |
| ;; |
| ESSID:*) |
| cell_essid=${scan#*ESSID:\"}; |
| cell_essid=${cell_essid%\"*}; |
| esac; |
| else |
| if [[ "${cell_essid#CrOS-test-}" != "$cell_essid" ]]; then |
| progress "Found test ESSID $cell_essid (Frequency: $cell_freq)"; |
| case "$cell_freq" in |
| 2*) |
| lofreq_aps=${lofreq_aps}${lofreq_aps:+ }${cell_essid}; |
| ;; |
| [45]*) |
| hifreq_aps=${hifreq_aps}${hifreq_aps:+ }${cell_essid}; |
| ;; |
| esac; |
| else |
| progress "Ignoring ESSID $cell_essid (Frequency: $cell_freq)"; |
| fi; |
| cell_essid=""; |
| cell_freq=""; |
| fi; |
| done < <(iwlist $iface scan); |
| } |
| |
| wifi_find_essid () { |
| iface=$1; |
| target=$2; |
| |
| progress Bringing up $iface; |
| ifconfig $iface up 1>&2; |
| progress Scanning for ESSID $target; |
| iwlist $iface scan | grep -q "ESSID:\"$target\""; |
| } |
| |
| wifi_strength () { |
| iface=$1; |
| result=; |
| macaddr=$(cat /sys/class/net/$iface/address) |
| gateway=$(ip route show dev $iface to match 0/0|\ |
| if read x x g x; then echo $g; fi); |
| |
| progress Allowing link $gateway strength/quality readings to stabilise; |
| ping -n -w 5 -c 5 $gateway 1>&2; |
| |
| progress Contacting AP at "/dev/tcp/$gateway/80" to collect TX strength; |
| if exec {http}<>/dev/tcp/$gateway/80; then |
| echo -e "GET /cgi-bin/txinfo HTTP/1.0\r\n\r" >&$http; |
| |
| while read mac strength other; |
| do |
| if [[ x${mac,,*} = x${macaddr,,*} ]]; then result=$strength; fi; |
| done <&$http; |
| fi; |
| |
| tx_db=${result:--100}" dBm"; |
| } |
| |
| wifi_associate () { |
| wifi_status $iface; |
| |
| essid=${2:-"NO-ESSID-SUPPLIED"}; |
| |
| if wifi_find_essid $iface $essid; then |
| SECONDS=0; |
| iwconfig $iface essid "$essid" 1>&2; |
| |
| until [[ x$if_essid = x$essid && x$if_ap != x'Not-Associated' ]]; do |
| wifi_status $iface; |
| progress "$SECONDS: $if_essid/$if_ap (want $essid)"; |
| sleep 2; |
| if [[ $SECONDS -ge 30 ]]; then if_ap=failed; fi; |
| done; |
| else |
| if_ap="Not-Found"; |
| fi |
| |
| test "$if_essid" = "$essid"; |
| } |
| |
| wifi_dhcp () { |
| iface=$1; |
| dhclient $iface \ |
| -sf /usr/local/sbin/dhclient-script \ |
| -lf /tmp/dhclient.leases 1>&2; |
| } |
| |
| emit_result () { |
| test=$2; |
| |
| if [[ "$1" = success ]]; then |
| |
| cat - <<EOF; |
| 802.11$test freq $if_freq quality $if_quality rx $if_signal tx $tx_db |
| EOF |
| |
| else |
| |
| cat - <<EOF; |
| 802.11$test freq 0 quality 0/70 rx -100 dBm tx -100 dBm |
| EOF |
| |
| fi; |
| } |
| |
| test_association () { |
| wlan_if=$1; |
| ap_ssid=$2; |
| mods=$3; |
| |
| if wifi_associate $wlan_if $ap_ssid; then |
| wifi_dhcp $wlan_if; |
| wifi_strength $wlan_if; |
| emit_result success $mods; |
| else |
| progress "WiFi Association failed for $wlan_if [$ap_ssid vs $if_ap]"; |
| emit_result failure $mods; |
| fi; |
| } |
| |
| output_to_vtx; |
| |
| wifi_status $1; # this will figure out all our initial if_… values |
| modulations=$if_80211; |
| |
| progress "Start: $iface ($if_mode/$if_80211) ap $if_ap essid '$if_essid'"; |
| progress "Shutting down shill"; |
| stop shill 1>&2 |
| |
| progress "Looking for test APs"; |
| wifi_scan $iface; |
| |
| progress "2.x GHz APs: $lofreq_aps"; |
| progress "4+ GHz APs: $hifreq_aps"; |
| |
| if contains_modulations $modulations bg; then |
| for ap in $lofreq_aps; do test_association $iface $ap bg; done; |
| fi |
| |
| if contains_modulations $modulations an; then |
| for ap in $hifreq_aps; do test_association $iface $ap an; done; |
| fi |
| |
| start shill 1>&2; |
| |
| close_vtx; |