# Copyright (c) 2004-2005 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# $Header: /var/cvsroot/gentoo-src/rc-scripts/net-scripts/net.modules.d/wpa_supplicant,v 1.2.2.17 2005/04/26 21:41:40 uberlord Exp $

# Contributed by Roy Marples (uberlord@gentoo.org)

# Fix any potential localisation problems
# Note that LC_ALL trumps LC_anything_else according to locale(7)
wpa_supplicant() {
	LC_ALL=C /usr/sbin/wpa_supplicant "$@"
}

wpa_cli() {
	LC_ALL=C /usr/bin/wpa_cli "$@"
}

# char* wpa_supplicant_provides(void)
#
# Returns a string to change module definition for starting up
wpa_supplicant_provides() {
	echo "wireless"
}

# void wpa_supplicant_depend(void)
#
# Sets up the dependancies for the module
wpa_supplicant_depend() {
	after macchanger
	before interface
}

# bool wpa_supplicant_check_installed(void)
#
# Returns 1 if wpa_supplicant is installed, otherwise 0
wpa_supplicant_check_installed() {
	local report=${1:-false} installed=0
	if [[ ! -x /usr/sbin/wpa_supplicant ]]; then
		installed=1
		${report} && eerror "For WPA support (wpa_supplicant) support, emerge net-wireless/wpa-supplicant"
	fi
	if [[ ! -e /proc/net/packet ]]; then
		installed=1
		${report} && eerror "wpa_supplicant requires Packet Socket (CONFIG_PACKET=y) enabled in the kernel"
	fi
	return ${installed}
}

# bool wpa_supplicant_check_depends(void)
#
# Checks to see if we have the needed functions
wpa_supplicant_check_depends() {
	local f

	for f in interface_exists interface_variable; do
		[[ $( type -t ${f} ) == function ]] && continue
		eerror "wpa_supplicant: missing required function ${f}\n"
		return 1
	done

	return 0
}

# bool wpa_supplicant_check_extensions(char *interface)
#
# Checks to see if wireless extensions are enabled on the interface
wpa_supplicant_check_extensions() {
	grep -q "${1}: " /proc/net/wireless
}

# char* wpa_supplicant_get_essid(char *interface)
#
# Gets the current ESSID of iface
wpa_supplicant_get_essid() {
	local i essid

	for (( i=0; i<5; i++ )); do
		essid=$( wpa_cli -i${1} status 2>/dev/null | awk -F= '/^ssid=/ { print $2 }' )
		if [[ -n ${essid} ]]; then
			echo ${essid}
			return 0
		fi
		sleep 1
	done

	return 1
}

# char* wpa_supplicant_get_ap_mac_address(char *interface)
#
# Returns the MAC address of the Access Point
# the interface is connected to
wpa_supplicant_get_ap_mac_address() {
	wpa_cli -i${1} status 2>/dev/null | awk -F= '/^bssid=/ { print toupper($2) }'
}

# bool wpa_supplicant_associated(char *interface)
#
# Returns 0 if we're associated correctly or 1 if not
# Note that just because we are associated does not mean we are using the
# correct encryption keys
wpa_supplicant_associated() {
	local -a status=( "$( wpa_cli -i${1} status | awk -F= '/^key_mgmt|^wpa_state|^EAP state/ { print "\""$2"\"" }' )" )

	case ${status[0]} in
		"NONE"	)		[[ ${status[1]} == "ASSOCIATED" || ${status[1]} == "COMPLETED" ]] ;;
		"IEEE 802.1X (no WPA)")	[[ ${status[2]} == "SUCCESS" ]] ;;
		*)			[[ ${status[1]} == "COMPLETED" ]] ;;
	esac

	return $?
}

# void wpa_supplicant_kill(char *interface, bool report)
#
# Kills any existing wpa_supplicant process on the interface
wpa_supplicant_kill() {
	local iface=$1 report=${2:-false}
	local pid=$( ps --no-headers -fC wpa_supplicant 2>/dev/null | awk '/-i'${iface}'/ { print $2 }' )

	if [[ -n ${pid} ]]; then
		${report} && ebegin "Stopping wpa_supplicant on ${iface}"
		kill -s TERM ${pid}
		${report} && eend 0
	fi

	# If wpa_supplicant exits uncleanly, we need to remove the stale dir
	[[ -S /var/run/wpa_supplicant/${iface} ]] && rm -f /var/run/wpa_supplicant/${iface}
}

# bool wpa_supplicant_associate(char *interface)
#
# Returns 0 if wpa_supplicant associates and authenticates to an AP
# otherwise, 1
wpa_supplicant_associate() {
	local iface=${1} ifvar=$( interface_variable ${1} ) timeout i

	eval timeout=\"\$\{wpa_timeout_${ifvar}:-60\}\"
	for (( i=0; i<${timeout}; i++ )); do
		wpa_supplicant_associated ${iface} && break
		sleep 1
	done
	if [[ ${i} == ${timeout} ]]; then
		if ! wpa_supplicant_associated ${iface}; then
			[[ ${background} != yes ]] && eend 1 "timed out"

			# We now need to kill the process
			wpa_supplicant_kill ${iface}

			return 1
		fi
	fi
}

# bool wpa_supplicant_pre_start(char *interface)
#
# Start wpa_supplicant on an interface and wait for association
# Returns 0 (true) when successful, non-zero otherwise
wpa_supplicant_pre_start() {
	local iface=$1 opts ifvar cfgfile=/etc/wpa_supplicant.conf timeout

	# We only work on wirelesss interfaces
	wpa_supplicant_check_extensions ${iface} || return 0

	# Kill off any existing wpa_supplicant on this interface
	# This is so we can re-read the configuration file and clean any stale
	# directories
	wpa_supplicant_kill ${iface} true

        # If wireless-tools is installed, try and apply our user config
	# This is needed for some drivers - such as hostap because they start
	# the card in Master mode which causes problems with wpa_supplicant.
	if [[ $( type -t iwconfig_defaults ) == "function" ]]; then
		iwconfig_defaults ${iface}
		iwconfig_user_config ${iface}
	fi

	ebegin "Starting wpa_supplicant on ${iface}"

	if [[ ! -f ${cfgfile} ]]; then
		eend 1 "configuration file ${cfgfile} not found!"
		return 1
	fi

	local ctrl_dir=$( sed -ne 's/[ \t]*#.*//g;s/[ \t]*$//g;s/^ctrl_interface=//p' ${cfgfile} )
	if [[ ${ctrl_dir} != "/var/run/wpa_supplicant" ]]; then
		eerror "${cfgfile} must set"
		eerror "  ctrl_interface=/var/run/wpa_supplicant"
		eend 1
		return 1
	fi

	ifvar=$( interface_variable ${iface} )
	eval opts=\" \$\{wpa_supplicant_${ifvar}\}\"
	[[ ${opts} != *' -D'* ]] && ewarn "wpa_supplicant_${ifvar} does not define a driver"
	
	# Some drivers require the interface to be up
	interface_up ${iface}

	if ! wpa_supplicant ${opts} -B -c/etc/wpa_supplicant.conf -i${iface} ; then
		eend 1
		return 1
	fi

	if ! wpa_cli -i${iface} status &>/dev/null ; then
		eend 1 "wpa_supplicant has exited unexpectedly"
		return 1
	fi

	eindent
	veinfo "Waiting for association"
	eend 0

	wpa_supplicant_associate ${iface} || return 1

	# Set ESSID for essidnet and report
	ESSID=$( wpa_supplicant_get_essid ${iface} )
	local -a status=( "$( wpa_cli -i${iface} status | awk -F= '/^bssid|^pairwise_cipher|^key_mgmt/ { print "\""$2"\"" }' )" )
	local mac=$( echo ${status[0]} | tr '[:lower:]' '[:upper:]' )
	einfo "${iface} connected to \"${ESSID//\\\\/\\\\}\" at ${mac}"

	if [[ ${status[2]} == NONE ]]; then
		if [[ ${status[1]} == NONE ]]; then
			ewarn "not using any encryption"
		else
			veinfo "using ${status[1]}"
		fi
	else
		veinfo "using ${status[2]}/${status[1]}"
	fi
	eoutdent

	return 0
}

# bool wpa_supplicant_post_stop(char *iface)
#
# Stops wpa_supplicant on an interface
# Returns 0 (true) when successful, non-zero otherwise
wpa_supplicant_post_stop() {
	wpa_supplicant_kill $1 true
	return 0
}
