# Uses wpa_supplicant to check for association to a network # wpa_check interface [timeout] wpa_check() { local timeout=0 INTERFACE="$1" TIMEOUT="${2:-15}" CONDITION="${3:-COMPLETED}" # CONDITION is required as wired connections are ready at ASSOCIATED not COMPLETED FS#20150 report_debug wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" status while (( timeout < TIMEOUT )); do ( # Sometimes wpa_supplicant isn't ready so silence errors for 2s only to avoid hiding real errors if (( timeout < 2 )); then eval $(wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" status 2> /dev/null | fgrep "wpa_state=") else eval $(wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" status | fgrep "wpa_state=") fi [[ "$wpa_state" = "$CONDITION" ]] ) && return 0 sleep 1 (( ++timeout )) done echo "$wpa_state" # wpa_cli -i "$INTERFACE" terminate >/dev/null 2>&1 # callers sometimes called stop_wpa, which does more but seems redundant # termination should either be handled properly here, or by callers stop_wpa "$INTERFACE" return 1 } start_wpa() { local INTERFACE="$1" WPA_CONF="$2" WPA_DRIVER="$3" shift 3 local WPA_OPTS="$*" if [[ -n "$WPA_CONF" ]]; then WPA_CONF="-c$WPA_CONF" else WPA_CONF="-C$WPA_CTRL_PATH" fi wpa_supplicant -B -P "/run/wpa_supplicant_${INTERFACE}.pid" -i "$INTERFACE" -D "$WPA_DRIVER" "$WPA_CONF" $WPA_OPTS sleep 1 if [[ ! -f "/run/wpa_supplicant_${INTERFACE}.pid" ]]; then return 1 fi } stop_wpa() { wpa_cli -p "$WPA_CTRL_PATH" -i "$1" terminate &> /dev/null sleep 1 # JP: need this else the file tends to disappear after [[ -f ... ]] but before cat... # see if [[ -f "/run/wpa_supplicant_$1.pid" ]]; then kill "$(cat "/run/wpa_supplicant_$1.pid")" &>/dev/null & fi } wpa_reconfigure() { local INTERFACE=$1 wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" reconfigure return $? } wpa_check_current_essid() { # usage: wpa_check_current_essid $interface $essid # check that wpa_supplicant is connected to the right essid local INTERFACE=$1 ESSID=$2 status status=$(wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" status | grep "^ssid=") if (( $? == 0 )) && [[ "$status" == "ssid=$ESSID" ]]; then return 0 else return 1 fi } wpa_find_essid() { # usage: wpa_find_essid $INTERFACE $ESSID # look for existence of a given essid. Assumes wpa_supplicant is # running result=$(wpa_supplicant_scan_and_find "$1" 5 "$2") ret=$? echo $result report_debug wpa_find_essid "\"$result\"" return $ret } wpa_find_ap() { # usage: wpa_find_essid $INTERFACE $ESSID # look for existence of a given essid. Assumes wpa_supplicant is # running bssid=${2,,} # set to lowercase result=$(wpa_supplicant_scan_and_find "$1" 1 "$bssid") ret=$? echo $result report_debug wpa_find_ap "\"$result\"" return $ret } wpa_supplicant_scan_and_find() { #usage: wpa_supplicant_scan_and_find $INTERFACE $FIELD $ITEM # field = 1 for bssid, 5 for essid # item = string to lookup local INTERFACE="$1" FIELD="$2" ITEM="$3" RETRIES=5 try scan_ok scan_ok=0 report_debug wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" scan wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" scan &> /dev/null for ((try=0; try < $RETRIES; try++)); do local found sleep 2 found=$(wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" scan_results | tail -n+2 | cut -f ${FIELD} | grep -F -x -m 1 "${ITEM}") (( $? == 0 )) && scan_ok=1 # ITEM has been found, echo it if [[ -n "$found" ]]; then echo "$found" return 0 fi wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" scan &> /dev/null done if (( $scan_ok != 1 )); then report_debug wpa_supplicant_scan_and_find "unable to retrieve scan results" fi return 1 } # Return a filename containing a list of network APs and ESSIDs found (sorted by decreasing signal strength) # list_networks interface list_networks() { wpa_supplicant_scan_info "$1" 1,5 } wpa_supplicant_scan_info() { local INTERFACE="$1" fields="$2" spawned_wpa=0 essids scan_wait # temp file used, as keeping ESSID's with spaces in their name in arrays # is hard, obscure and kinda nasty. This is simpler and clearer. [[ -z "$INTERFACE" ]] && return 1 essids=$(mktemp --tmpdir essid.XXXXXXXX) if [[ "$(wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" ping 2> /dev/null)" != "PONG" ]]; then start_wpa "$INTERFACE" "" "${WPA_DRIVER:-nl80211,wext}" || return 1 spawned_wpa=1 fi wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" scan &> /dev/null # Wait at least 2 seconds for scan results sleep 2 # Sometimes, that is not enough (FS#29946) for ((scan_wait = 2; scan_wait < 10; scan_wait++)); do wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" status | grep -q "wpa_state=SCANNING" || break sleep 1 done wpa_cli -p "$WPA_CTRL_PATH" -i "$INTERFACE" scan_results | grep -v "^Selected" | grep -v "^bssid" | sort -rn -k3 | sort -u -k5 | sort -rn -k3 | cut -f"$fields" > "$essids" # Fields are tab delimited # Remove extraneous output from wpa_cli # Sort by strength # Remove duplicates # Re-sort by strength as the removal disorders the list # Cut to the AP/essid fields only (( $spawned_wpa == 1 )) && stop_wpa "$INTERFACE" # File of 0 length, ie. no ssid's. if [[ ! -s "$essids" ]]; then rm -f "$essids" return 1 fi echo $essids return 0 } # Requires already loaded profile make_wpa_config_file() { local interface=$1 local WPA_CONF="$STATE_DIR/wpa.${interface}" # substitute spaces out # make empty tmp dir with correct permissions, rename it check_make_state_dir mkdir -p /run/wpa_supplicant rm -rf "$WPA_CONF" mv -f "$(mktemp -d --tmpdir=$STATE_DIR)" "$WPA_CONF" || return 1 echo "ctrl_interface=/run/wpa_supplicant" >> "$WPA_CONF/wpa.conf" # we know $WPA_CONF now has no spaces, but it may have other nasty chars, so still needs to be quoted echo "ctrl_interface_group=${WPA_GROUP:-wheel}" >> "$WPA_CONF/wpa.conf" [[ $WPA_COUNTRY ]] && echo "country=$WPA_COUNTRY" >> "$WPA_CONF/wpa.conf" [[ -n "$ADHOC" ]] && echo "ap_scan=2" >> "$WPA_CONF/wpa.conf" echo "$WPA_CONF/wpa.conf" } # Requires already loaded profile make_wpa_config() { case $SECURITY in none|wep|wpa) case "${ESSID_TYPE:-ascii}" in ascii) echo "ssid=\"$ESSID\"" ;; hex) # Hex ESSID is written unquoted ans lowercase (FS#24333) echo "ssid=${ESSID,,}" ;; *) report_fail "ESSID_TYPE must be set to 'ascii' or 'hex'." return 1 ;; esac if [[ -n "$AP" ]]; then echo "bssid=${AP,,}" fi [[ -n "$ADHOC" ]] && echo "mode=1" ;; wpa-configsection) echo "$CONFIGSECTION" ;; *) return 1 ;; esac # Key management case $SECURITY in none) echo "key_mgmt=NONE" ;; wep) echo "key_mgmt=NONE" echo "wep_tx_keyidx=0" if [[ ${KEY:0:2} == "s:" ]]; then # TODO: does wpa_supplicant handle this as expected? echo "wep_key0=\"${KEY:2}\"" else echo "wep_key0=$KEY" fi ;; wpa) echo "proto=RSN WPA" if [[ "${#KEY}" -eq 64 ]]; then echo "psk=$KEY" else echo "psk=\"$KEY\"" fi ;; esac # Hidden SSID if checkyesno ${HIDDEN:-no}; then echo "scan_ssid=1" fi } # vim: ft=sh ts=4 et sw=4 tw=0: