summaryrefslogtreecommitdiffstats
path: root/src/lib/wpa
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/wpa')
-rw-r--r--src/lib/wpa233
1 files changed, 233 insertions, 0 deletions
diff --git a/src/lib/wpa b/src/lib/wpa
new file mode 100644
index 0000000..65ef8f9
--- /dev/null
+++ b/src/lib/wpa
@@ -0,0 +1,233 @@
+## /usr/lib/network/globals needs to be sourced before this file
+
+
+## Wrapper around wpa_cli to deal with supplicant configurations that set a
+## non-standard control path
+# $1: interface name
+# $2...: call to the supplicant
+wpa_call() {
+ local args=( "-i" "$1" )
+ shift
+
+ if [[ $WPA_CTRL_DIR ]]; then
+ args+=("-p" "$WPA_CTRL_DIR")
+ elif [[ $WPAConfigFile ]] && grep -q "^[[:space:]]*ctrl_interface=" "$WPAConfigFile"; then
+ WPA_CTRL_DIR=$(grep -m1 "^[[:space:]]*ctrl_interface=" "$WPAConfigFile")
+ WPA_CTRL_DIR=${WPA_CTRL_DIR#*ctrl_interface=}
+ if [[ $WPA_CTRL_DIR == DIR=* ]]; then
+ WPA_CTRL_DIR=${WPA_CTRL_DIR:4}
+ WPA_CTRL_DIR=${WPA_CTRL_DIR%% GROUP=*}
+ fi
+ args+=( "-p" "$WPA_CTRL_DIR" )
+ fi
+ do_debug wpa_cli "${args[@]}" "$@"
+}
+
+## Check if an instance of the wpa supplicant is active on an interface
+# $1: interface name
+wpa_is_active(){
+ [[ $(wpa_call "$1" ping 2> /dev/null) == "PONG" ]]
+}
+
+## Retrieves the state of an instance of the wpa supplicant
+## Displays one of: DISCONNECTED, INTERFACE_DISABLED, INACTIVE, SCANNING,
+## AUTHENTICATING, ASSOCIATING, ASSOCIATED, 4WAY_HANDSHAKE,
+## GROUP_HANDSHAKE, COMPLETED
+# $1: interface name
+wpa_get_state() {
+ local state
+ state=$(wpa_call $1 status | grep -m1 "^wpa_state=") || return 1
+ echo ${state#wpa_state=}
+}
+
+## Waits until the wpa supplicant reaches a listed state
+# $1: timeout
+# $2: interface name
+# $3...: accepted states
+wpa_wait_until_state() {
+ local timeout=$1 interface=$2 states=( "${@:3}" )
+ timeout_wait "$timeout" \
+ 'in_array "$(wpa_get_state "$interface")" "${states[@]}"'
+}
+
+## Waits while the wpa supplicant is in a listed state
+# $1: timeout
+# $2: interface name
+# $3...: rejected states
+wpa_wait_while_state() {
+ local timeout=$1 interface=$2 states=( "${@:3}" )
+ timeout_wait "$timeout" \
+ '! in_array "$(wpa_get_state "$interface")" "${states[@]}"'
+}
+
+## Start an instance of the wpa supplicant
+# $1: interface name
+# $2: interface driver(s)
+# $3: configuration file
+wpa_start() {
+ local interface=$1 driver=$2 configuration=$3
+ local pidfile="/run/wpa_supplicant_$interface.pid"
+
+ if [[ $configuration ]]; then
+ configuration="-c$configuration"
+ else
+ WPA_CTRL_DIR="/run/wpa_supplicant"
+ configuration="-C$WPA_CTRL_DIR"
+ fi
+
+ do_debug wpa_supplicant -B -P "$pidfile" -i "$interface" -D "$driver" \
+ "$configuration" $WPAOptions
+
+ # Wait up to one second for the pid file to appear
+ if ! timeout_wait 1 '[[ -f "$pidfile" ]]'; then
+ # Remove the configuration file if it was generated
+ configuration="$STATE_DIR/wpa_supplicant_$interface.conf"
+ [[ -f $configuration && -O $configuration ]] && rm -f "$configuration"
+ return 1
+ fi
+}
+
+## Stop an instance of the wpa supplicant
+# $1: interface name
+wpa_stop() {
+ local interface=$1 config_file="$STATE_DIR/wpa_supplicant_$1.conf"
+ # We need this as long as wpa_cli has a different default than netctl
+ if [[ -z $WPA_CTRL_DIR && -z $WPAConfigFile ]]; then
+ WPA_CTRL_DIR="/run/wpa_supplicant"
+ fi
+
+ # Done if wpa_supplicant is already terminated for this interface
+ [[ -z $WPA_CTR_DIR || -e "$WPA_CTRL_DIR/$interface" ]] || return
+
+ wpa_call "$interface" terminate > /dev/null
+ [[ -f $config_file && -O $config_file ]] && rm -f "$config_file"
+
+ # Wait up to one second for the pid file to be removed
+ if ! timeout_wait 1 '[[ ! -f "/run/wpa_supplicant_$interface.pid" ]]'; then
+ kill "$(< "/run/wpa_supplicant_$interface.pid")" &> /dev/null
+ fi
+}
+
+## Scan for networks within range
+# $1: interface name
+# $2: fields of the wpa_supplicant scan_results
+wpa_supplicant_scan() {
+ local interface=$1 fields=$2 spawned_wpa=0 essids
+ # 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.
+
+ [[ $interface ]] || return 1
+ essids=$(mktemp --tmpdir essid.XXXXXXXX)
+
+ if ! wpa_is_active "$interface"; then
+ wpa_start "$interface" "${WPADriver:-nl80211,wext}" || return 1
+ spawned_wpa=1
+ fi
+
+ wpa_call "$interface" scan > /dev/null
+ # Wait at least 3 seconds for scan results
+ sleep 3
+ # Sometimes, that is not enough
+ wpa_wait_while_state 7 "$interface" "SCANNING"
+ wpa_call "$interface" scan_results |
+ tail -n+2 |
+ 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 )) && wpa_stop "$interface"
+
+ # File of 0 length: no ssid's
+ if [[ ! -s "$essids" ]]; then
+ rm -f "$essids"
+ return 1
+ fi
+
+ echo "$essids"
+}
+
+## Print a string within quotes, unless it starts with the "-modifier
+## Quoted: string "string" '""string"'
+## Non-quoted: \"string "\"string" '"string'
+# $1: string
+wpa_quote() {
+ local string=$1
+ if [[ $string == \"* ]]; then
+ printf '%s' "${string:1}"
+ else
+ printf '"%s"' "$string"
+ fi
+}
+
+## Create a configuration file for wpa_supplicant without any network blocks
+# $1: interface name
+wpa_make_config_file() {
+ local config_file="$STATE_DIR/wpa_supplicant_$1.conf"
+
+ if [[ -e $config_file ]]; then
+ report_debug "The anticipated filename '$config_file' is not available"
+ return 1
+ fi
+ mkdir -p "$STATE_DIR" /run/wpa_supplicant
+ if ! : > "$config_file"; then
+ report_debug "Could not create the configuration file '$config_file'"
+ return 1
+ fi
+ chmod 600 "$config_file"
+
+ echo "ctrl_interface=/run/wpa_supplicant" >> "$config_file"
+ echo "ctrl_interface_group=${WPAGroup:-wheel}" >> "$config_file"
+ [[ $Country ]] && echo "country=$Country" >> "$config_file"
+ if is_yes "${AdHoc:-no}"; then
+ echo "ap_scan=2" >> "$config_file"
+ fi
+ echo "$config_file"
+}
+
+## Generate a network block for wpa_supplicant
+# $Security: type of wireless security
+wpa_make_config_block() {
+ case $Security in
+ none)
+ echo "key_mgmt=NONE"
+ ;;
+ wep)
+ echo "key_mgmt=NONE"
+ echo "wep_tx_keyidx=0"
+ echo "wep_key0=$(wpa_quote "$Key")"
+ ;;
+ wpa)
+ echo "proto=RSN WPA"
+ if [[ "${#Key}" -eq 64 ]]; then
+ echo "psk=$Key"
+ else
+ echo "psk=$(wpa_quote "$Key")"
+ fi
+ ;;
+ wpa-configsection)
+ printf "%s\n" "${WPAConfigSection[@]}"
+ return
+ ;;
+ *)
+ report_error "Unsupported security setting: '$Security'"
+ return 1
+ ;;
+ esac
+
+ echo "ssid=$(wpa_quote "$ESSID")"
+ [[ $AP ]] && echo "bssid=${AP,,}"
+ is_yes "${Hidden:-no}" && echo "scan_ssid=1"
+ is_yes "${AdHoc:-no}" && echo "mode=1"
+ [[ $Priority ]] && echo "priority=$Priority"
+}
+
+
+# vim: ft=sh ts=4 et sw=4: