#!/bin/sh

# Copyright (C) 2006 Kel Modderman <kelrin@tpg.com.au>
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# On Debian GNU/Linux systems, the text of the GPL license can be
# found in /usr/share/common-licenses/GPL.

if [ -z "$1" -o -z "$2" ]; then
	echo "Usage: $0 IFACE ACTION"
	exit 1
fi

# network interface is first argument
IFACE="$1"
# action in [CONNECTED|DISCONNECTED|stop|reload] is second argument
ACTION="$2"

# log actions to file
WPA_LOGFILE="/var/log/wpa_action.log"
# lock wpa_cli ACTION events
WPA_LOCKFILE="/var/lock/wpa_action.$IFACE.lock"

if [ -n "$IF_WPA_ROAM_MAINT_DEBUG" ]; then
	set -x
fi

# ACTION locking
# the purpose of the following locking function is to ensure we are not
# interrupting the currently active action
action_lock () {
	# test and set lock file in an atomic action
	if ! (umask 222; echo "$$" >"$WPA_LOCKFILE") 2>/dev/null; then
		# action is locked
		echo "$0 is locked on $IFACE, \"$ACTION\" ACTION aborted"
		exit 0
	fi
}

action_wait () {
	if [ -e "$WPA_LOCKFILE" ]; then
		echo "Waiting for current ACTION to finish before processing \"$ACTION\" ACTION"
		local ACTION_WAIT="0" MAX_ACTION_WAIT="60"
		until [ ! -e "$WPA_LOCKFILE" ]; do
			if [ "$ACTION_WAIT" -ge "$MAX_ACTION_WAIT" ]; then
				rm -f "$WPA_LOCKFILE"
			else
				ACTION_WAIT=$(($ACTION_WAIT+1))
			fi
			sleep 1
		done
	fi
}

action_unlock () {
	# clean up lockfile
	if [ -e "$WPA_LOCKFILE" ]; then
		rm -f "$WPA_LOCKFILE"
	fi
}

# Log functions
log_init () {
	exec >> "$WPA_LOGFILE" 2>&1
}

log_action () {
	echo "########## $(date +"%H:%M:%S  %Y-%m-%d") ##########"
	echo "IFACE=$IFACE ACTION=$ACTION"
}

log_environment () {
	echo "WPA_ID=$WPA_ID WPA_ID_STR=$WPA_ID_STR"
	echo "WPA_CTRL_DIR=$WPA_CTRL_DIR"
}

check_ifupdown () {
	# interfaces file
	if [ -e /etc/network/interfaces ]; then
		INTERFACES_FILE="/etc/network/interfaces"
	else
		echo "Cannot locate ifupdown's \"interfaces\" file, $IFACE will not be configured"
		return 1
	fi

	# ifstate file
	if [ -e /etc/network/run/ifstate ]; then
		# debian's ifupdown
		IFSTATE_FILE="/etc/network/run/ifstate"
	elif [ -e /var/run/network/ifstate ]; then
		# ifstate file lives in /var/run on Ubuntu
		IFSTATE_FILE="/var/run/network/ifstate"
	else
		echo "Cannot locate ifupdown's \"ifstate\" file, $IFACE will not be configured"
		return 1
	fi

	return 0
}

ifup () {
	# ensure this is not present in environment and private to function
	local WPA_LOGICAL_IFACE
	# If "wpa-script-priority 1" is set in /e/n/i we'll skip the id_str matching
	if [ -z "$IF_WPA_MAPPING_SCRIPT_PRIORITY" -a -n "$WPA_ID_STR" ]; then
		WPA_LOGICAL_IFACE="$WPA_ID_STR"
		echo "Mapping logical interface via id_str: $WPA_LOGICAL_IFACE"
	fi
	
	if [ -z "$WPA_LOGICAL_IFACE" -a -n "$IF_WPA_MAPPING_SCRIPT" ]; then
		# find a suitable WPA_LOGICAL_IFACE by running the mapping script
		echo "Mapping logical interface via wpa-mapping-script: $IF_WPA_MAPPING_SCRIPT"
		
		# ensure this is private to function
		local WPA_MAP_STDIN
		WPA_MAP_STDIN=$(set | sed -n -e 's/^\(IF_WPA_MAP[0-9]*\)=.*/echo\ \$\1/p')
		if [ -n "$WPA_MAP_STDIN" ]; then
			WPA_LOGICAL_IFACE=$(eval "$WPA_MAP_STDIN" | "$IF_WPA_MAPPING_SCRIPT" "$IFACE")
		else		
			WPA_LOGICAL_IFACE=$("$IF_WPA_MAPPING_SCRIPT" "$IFACE")
		fi
		
		if [ -n "$WPA_LOGICAL_IFACE" ]; then
			echo "Mapping script result: $WPA_LOGICAL_IFACE"
		else
			echo "Mapping script failed."
		fi
	fi

	# use fallback mapping if present
	if [ -z "$WPA_LOGICAL_IFACE" ]; then
		if [ -n "$IF_WPA_ROAM_DEFAULT_IFACE" ]; then
			# wpa-roam-default-iface
			WPA_LOGICAL_IFACE="$IF_WPA_ROAM_DEFAULT_IFACE"
			echo "Using wpa-roam-default-iface: $WPA_LOGICAL_IFACE"
		else
			WPA_LOGICAL_IFACE="default"
			echo "Using fallback logical interface: $WPA_LOGICAL_IFACE"
		fi
	fi

	# grep for matching LOGICAL interface stanza in interfaces file
	if [ -n "$WPA_LOGICAL_IFACE" ]; then
		if grep --quiet "^iface $WPA_LOGICAL_IFACE inet" "$INTERFACES_FILE"; then
			echo "ifup $IFACE=$WPA_LOGICAL_IFACE"
			# Map to LOGICAL and bring up the IFACE
			if grep --quiet "^$IFACE=$IFACE" "$IFSTATE_FILE"; then
				# Force settings over the unconfigured "master" IFACE
				/sbin/ifup --force "$IFACE=$WPA_LOGICAL_IFACE"
			else
				/sbin/ifup "$IFACE=$WPA_LOGICAL_IFACE"
			fi
		else
			echo "No network defined for \"$WPA_LOGICAL_IFACE\" in \"$INTERFACES_FILE\""
		fi
	else
		echo "No suitable logical interface mapping for ifupdown to configure"
	fi
}

ifdown () {
	# grep for IFACE state in ifstate file, is it up or down?
	if grep --quiet "^$IFACE" "$IFSTATE_FILE"; then
		echo "ifdown $IFACE"
		# Take down the IFACE
		/sbin/ifdown "$IFACE"
	else
		echo "Ignoring request to take \"$IFACE\" down, it is not up"
	fi
}

iface_up () {
	# use iproute if installed
	if [ -x /sbin/ip ]; then
		# flush previous protocol address
		# it almost never needs flushing, so keep it quiet
		/sbin/ip addr flush dev "$IFACE" 2>/dev/null
		# Keep IFACE in 'up' operstate
		/sbin/ip link set "$IFACE" up
	else
		# Keep IFACE in 'up' operstate
		/sbin/ifconfig "$IFACE" up
	fi
}

# wpa_cli wrapper, ctrl_iface path and interface supplied
# cannot be used for interactive actions, environmental variableWPA_CTRL_DIR is not present
wpa_cli () {
	LC_ALL=C /sbin/wpa_cli -p $WPA_CTRL_DIR -i $IFACE "$@"
}

kill_wpa_cli () {
	WPA_CLI_PNAME="wpa_cli"
	WPA_CLI_PIDFILE="/var/run/wpa_action.$IFACE.pid"
	# Kill wpa_cli daemon
	if [ -f "$WPA_CLI_PIDFILE" ]; then
		start-stop-daemon --stop --oknodo --verbose \
			--name "$WPA_CLI_PNAME" --pidfile "$WPA_CLI_PIDFILE"
	
		if [ -f "$WPA_CLI_PIDFILE" ]; then
			rm -f "$WPA_CLI_PIDFILE"
		fi
	fi
}

kill_wpa_supplicant () {
	WPA_SUP_PNAME="wpa_supplicant"
	WPA_SUP_PIDFILE="/var/run/wpa_supplicant.$IFACE.pid"
	# Kill wpa_supplicant daemon
	if [ -f "$WPA_SUP_PIDFILE" ]; then
		start-stop-daemon --stop --oknodo --verbose \
			--name "$WPA_SUP_PNAME" --pidfile "$WPA_SUP_PIDFILE"
	
		if [ -f "$WPA_SUP_PIDFILE" ]; then
			rm -f "$WPA_SUP_PIDFILE"
		fi
	fi
}

reload_wpa_supplicant () {
	WPA_SUP_PNAME="wpa_supplicant"
	WPA_SUP_PIDFILE="/var/run/wpa_supplicant.$IFACE.pid"
	# Reload wpa_supplicant's configuration file
	if [ -f "$WPA_SUP_PIDFILE" ]; then
		echo "Reloading wpa_supplicant configuration file via HUP signal"
		start-stop-daemon --stop --signal HUP \
			--name "$WPA_SUP_PNAME" --pidfile "$WPA_SUP_PIDFILE"
	else
		echo "Cannot $ACTION, $WPA_SUP_PIDFILE does not exist"
	fi
}

case "$ACTION" in
	"CONNECTED")
		log_init
		action_lock
		log_action
		log_environment
		if check_ifupdown; then
			ifup
		fi
		wpa_cli status
		action_unlock
		;;

	"DISCONNECTED")
		log_init
		action_lock
		log_action
		log_environment
		if check_ifupdown; then
			ifdown
			iface_up
		fi
		action_unlock
		;;

	"stop"|"down")
		kill_wpa_cli
		action_wait
		if check_ifupdown; then
			ifdown
		fi
		kill_wpa_supplicant
		log_init
		log_action
		;;

	"restart"|"reload")
		reload_wpa_supplicant
		log_init
		log_action
		;;

	*)
		echo "Unknown action: \"$ACTION\""
		exit 1
		;;
esac

exit 0
