Bash程序  |  284行  |  6.54 KB

#!/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;