#!/bin/sh
#Copyright (C) BlueWave Projects and Services 2015-2022
#This software is released under the GNU GPL license.
#
# mesh11sd daemon
#
version="1.2.0"

get_mesh_iflist () {
	iflist=""
	all_ifcs=$(iw dev | awk -F "Interface " '$2>0{printf "%s " $2}')

	for iface in $all_ifcs; do
		iftype=$(iw dev $iface info | grep "type" | awk '{printf "%s", $2}')

		if [ "$iftype" = "mesh" ]; then
			iflist="$iflist $iface"
		fi
	done
}

check_mesh_phantom () {
	nodelist=$(iw dev $iface mpath dump | awk '{printf "%s,%s ", $1, $2}')

	debugtype="debug"
	syslogmessage="nodelist [$nodelist]"
	write_to_syslog

	for node in $nodelist; do
		phantom=$(echo "$node" | awk -F"," '$2 == "00:00:00:00:00:00" {printf $1 }')

		if [ ! -z "$phantom" ]; then
			debugtype="warn"
			syslogmessage="Phantom meshnode detected [$phantom] - deleting"
			write_to_syslog
			iw dev $iface mpath del $phantom
		fi
	done
}

get_params () {
	params=$(iw dev $iface mesh_param dump 2> /dev/null)

	if [ "$?" != 0 ]; then
		buffer="/tmp/"$(date | sha256sum | awk '{printf $1}')
		param_list=$(iw dev $iface get mesh_param 2> /dev/null | grep " - " | awk -F" - " '{printf "%s ", $2}')
		params=""


		for param in $param_list; do
			paramval=$(iw dev $iface get mesh_param $param 2> /dev/null)
			if [ "$?" = 0 ]; then
				paramline="$param = $paramval"
				echo "$paramline" >> $buffer
			fi
		done



		if [ -e "$buffer" ]; then
			params=$(cat $buffer)
			rm "$buffer"
		fi
	fi
}

check_mesh_params () {

	for param in $params; do

		conf=$(uci get $uciname.$param 2> /dev/null)

		if [ -z "$conf" ]; then
			continue
		fi

		param_value=$(iw dev $iface get mesh_param $param | awk '{printf "%s", $1}')

		if [ -z "$param_value" ]; then
			debugtype="info"
			syslogmessage="Failed to get current value of $param, mesh interface not established."
			write_to_syslog
			continue
		fi

		if [ "$param_value" != "$conf" ]; then
			debugtype="info"
			syslogmessage="Old value:$param=$param_value, Setting new value:$param=$conf"
			write_to_syslog
			iwstatus=$(iw dev $iface set mesh_param $param "$conf" 2>&1)

			if [ ! -z "$iwstatus" ]; then
				debugtype="err"
				syslogmessage="$param: $iwstatus"
				write_to_syslog
			fi
		fi
	done
}

restart_mesh () {
	wifi
}

get_current_setup () {
	enabled=$(uci get mesh11sd.setup.enabled 2> /dev/null)

	if [ -z "$enabled" ]; then
		enabled=1
	fi

	debuglevel=$(uci get mesh11sd.setup.debuglevel 2> /dev/null)

	if [ -z "$debuglevel" ]; then
		debuglevel=1
	fi

	checkinterval=$(uci get mesh11sd.setup.checkinterval 2> /dev/null)

	if [ -z "$checkinterval" ] || [ "$checkinterval" -lt 10 ]; then
		checkinterval=10
	fi

	interface_timeout=$(uci get mesh11sd.setup.interface_timeout 2> /dev/null)

	if [ -z "$interface_timeout" ] || [ "$interface_timeout" -lt 10 ]; then
		interface_timeout=10
	fi
}

wait_for_interface () {
	local ifname="$1"
	local timeout=$interface_timeout

	for i in $(seq $timeout); do
		if [ $(ip link show $ifname 2> /dev/null | grep -c -w "state UP") -eq 1 ]; then
			ifstatus="up"
			break
		fi
		sleep 1
		if [ $i == $timeout ] ; then
			syslogmessage="$ifname is not up - giving up for now."
			debugtype="notice"
			write_to_syslog
			ifstatus="down"
		fi
	done
}

# Write debug message to syslog
# $syslogmessage contains the string to log
# $debugtype contains the debug level string: debug, info, warn, notice, err, emerg.
write_to_syslog() {

	if [ ! -z "$syslogmessage" ]; then

		case $debugtype in
			"emerg") debugnum=0;;
			"err") debugnum=0;;
			"notice") debugnum=1;;
			"warn") debugnum=1;;
			"info") debugnum=2;;
			"debug") debugnum=3;;
			*) debugnum=1; debugtype="notice";;
		esac

		if [ "$debuglevel" -ge "$debugnum" ]; then
			echo "$syslogmessage" | logger -p "daemon.$debugtype" -s -t "mesh11sd[$mesh11sdpid]"
		fi
	fi
}


##############
# Start point
##############

mesh11sdpid=$(pgrep -f "/bin/sh /usr/sbin/mesh11sd")
get_current_setup

if [ -z "$1" ] || [ "$1" = "-h" ] || [ $1 = "--help" ] || [ $1 = "help" ]; then
	echo "
  Usage: mesh11sd [option] [argument...]]

  Option: -h --help help
  Returns: This help information

  Option: -v --version version
  Returns: The mesh11sd version

  Option: debuglevel
  Argument: 0 - silent, 1 - notice, 2 - info, 3 - debug
  Returns: The mesh11sd debug level

  Option: enable
  Returns: \"1\" and exit code 0 if successful, exit code 1 if was already enabled

  Option: disable
  Returns: \"0\" and exit code 0 if successful, exit code 1 if was already disabled

  Option: status
  Returns: the mesh status in json format
"

elif [ "$1" = "-v" ] || [ $1 = "--version" ] || [ $1 = "version" ]; then
	echo "mesh11sd version $version"

elif [ "$1" = "debuglevel" ]; then

	if [ -z "$2" ];then
		echo "$debuglevel"
	else

		if [ "$2" -ge 0 ] && [ "$2" -le 3 ]; then
			ucibatch="set mesh11sd.setup.debuglevel='$2'"
			echo "$ucibatch" | uci batch
			echo "$2"
		else
			echo "Invalid debuglevel requested"
		fi
	fi

elif [ "$1" = "enable" ]; then

	if [ "$enabled" -eq 1 ]; then
		echo "1"
		exit 1
	else
		ucibatch="set mesh11sd.setup.enabled='1'"
		echo "$ucibatch" | uci batch
		echo "1"
		exit 0
	fi

elif [ "$1" = "disable" ]; then

	if [ "$enabled" -eq 0 ]; then
		echo "0"
		exit 1
	else
		ucibatch="set mesh11sd.setup.enabled='0'"
		echo "$ucibatch" | uci batch
		echo "0"
		exit 0
	fi

elif [ "$1" = "status" ]; then
	# get list of interfaces
	get_mesh_iflist
	service=$(/etc/init.d/mesh11sd status)

	echo "{"
	echo "  \"setup\":{"
	echo "    \"version\":\"$version\","
	echo "    \"enabled\":\"$enabled\","
	echo "    \"service\":\"$service\","
	echo "    \"checkinterval\":\"$checkinterval\","
	echo "    \"interface_timeout\":\"$interface_timeout\","
	echo "    \"debuglevel\":\"$debuglevel\""
	echo "  }"
	echo "  \"interfaces\":{"

	for iface in $iflist; do
		wait_for_interface "$iface"

		if [ "$ifstatus" = "down" ]; then
			continue
		fi

		# get list of mesh parameters for this interface
		get_params

		if [ -z "$params" ]; then
			# this is not a mesh interface
			continue
		else
			params=$(echo "$params" | awk -F" " '{printf "      \"%s\":\"%s\",\n", $1, $3}')
			echo "    \"$iface\":{"
			echo "$params"

			meshconfig=$(uci show wireless | grep "ifname='$iface'" | awk -F ".ifname='$iface'" '{printf "%s", $1}')

			if [ -z "$meshconfig" ]; then
				echo "      \"interface\":\"unmanaged\""
				echo "    }"
			else
				device=$(uci get $meshconfig.device 2> /dev/null)
				channel=$(uci show wireless | grep "$device.channel" | awk -F "'" '{printf "%s", $2}')
				meshid=$(uci get $meshconfig.mesh_id 2> /dev/null)

				echo "      \"mesh_id\":\"$meshid\","
				echo "      \"device\":\"$device\","
				echo "      \"channel\":\"$channel\","

				tx_packets=$(devstatus $iface | grep "tx_packets" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"tx_packets\":\"$tx_packets\","

				tx_bytes=$(devstatus $iface | grep "tx_bytes" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"tx_bytes\":\"$tx_bytes\","

				rx_packets=$(devstatus $iface | grep "rx_packets" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"rx_packets\":\"$rx_packets\","

				rx_bytes=$(devstatus $iface | grep "rx_bytes" | awk -F": " '{print $2}' | tr -d ",")
				echo "      \"rx_bytes\":\"$rx_bytes\","

				peernum=0
				peers=$(iw dev $iface mpath dump | grep -c -w $iface)
				macrouting=$(iw dev $iface mpath dump | awk -F" " 'NR>1 {printf "%s/%s ",$1,$2}')

				echo "      \"active_peers\":\"$peers\","
				echo "      \"peers\":{"

				for peer in $macrouting; do
					peernum=$((peernum+1))
					peermac=$(echo "$peer" | awk -F"/" '{printf "%s", $1}')
					echo "        \"$peermac\":{"
					next_hop=$(echo "$peer" | awk -F"/" '{printf "%s", $2}')
					echo "          \"next_hop\":\"$next_hop\""

					if [ "$peers" = "$peernum" ]; then
						echo "        }"
					else
						echo "        },"
					fi
				done
				echo "      }"

				stanum=0
				stations=$(iw dev $iface mpp dump | grep -c -w $iface)
				starouting=$(iw dev $iface mpp dump | awk -F" " 'NR>1 {printf "%s/%s ",$1,$2}')

				echo "      \"active_stations\":\"$stations\","
				echo "      \"stations\":{"

				for sta in $starouting; do
					stanum=$((stanum+1))
					stamac=$(echo "$sta" | awk -F"/" '{printf "%s", $1}')
					proxy_node=$(echo "$sta" | awk -F"/" '{printf "%s", $2}')

					echo "        \"$stamac\":{"
					echo "          \"proxy_node\":\"$proxy_node\""

					if [ "$stations" = "$stanum" ]; then
						echo "        }"
					else
						echo "        },"
					fi
				done
				echo "      }"
				echo "    }"
			fi
		fi
	done

	echo "  }"
	echo "}"

elif [ "$1" = "daemon" ]; then
	uci revert mesh11sd
	debugtype="notice"
	syslogmessage="mesh11sd is in startup"
	write_to_syslog

	#Default flags
	startup=4 # bit 2
	statusmode=2 # bit 1
	enabled=1 #bit 0

	# Initial conditions
	statusmode=0
	changed=0

	# Initial Mode Flag
	mode=$(($startup + $statusmode + $enabled ))
	lastmode=0

	# Wait one checkinterval before starting the main loop
	sleep $checkinterval

	while true; do

		get_current_setup
		mode=$(($startup + $statusmode + $enabled))

		syslogmessage=""

		if [ $mode -eq 5 ]; then
			# startup=4, statusmode=0, enabled=1
			startup=0
			statusmode=0
			mode=1
			syslogmessage="mesh11sd v$version has started: mesh management mode $mode"

		elif [ $mode -eq 4 ]; then
			# startup=4, statusmode=0, enabled=0
			startup=0
			statusmode=2
			mode=0
			syslogmessage="mesh11sd v$version has started: mesh status mode $mode"

		elif [ $mode -eq 3 ]; then
			# startup=0, statusmode=2, enabled=1
			startup=0
			statusmode=0
			mode=1
			syslogmessage="mesh11sd v$version has started: mesh management mode $mode"

		elif [ $mode -eq 2 ]; then
			# startup=0, statusmode=2, enabled=0
			startup=0
			statusmode=2
			mode=0
			syslogmessage="mesh11sd v$version has started: mesh status mode $mode"

		elif [ $mode -eq 1 ]; then
			# startup=0, statusmode=0, enabled=1
			startup=0
			statusmode=0
			mode=1
			syslogmessage="mesh11sd v$version has started, mesh management mode $mode"

		elif [ $mode -eq 0 ]; then
			# startup=0, statusmode=0, enabled=0
			startup=0
			statusmode=2
			mode=0
			syslogmessage="mesh11sd v$version has started: mesh status mode $mode"
		fi

		if [ $mode -ne $lastmode ]; then
			debugtype="notice"
			write_to_syslog
		fi

		lastmode=$mode
		meshindex=0

		if [ "$enabled" = 1 ]; then
			#get list of mesh configs
			meshconfigs=$(uci show wireless 2> /dev/null | grep "mode='mesh'" | awk -F ".mode='mesh'" '{printf "%s " $1}')

			if [ ! -z "$meshconfigs" ]; then
				for meshconfig in $meshconfigs; do
					ifname=$(uci get $meshconfig.ifname 2> /dev/null)

					if [ -z "$ifname" ] || [ "$ifname" != "mesh$meshindex" ]; then
						# No interface name in config, so add one
						ucibatch="set $meshconfig.ifname='mesh$meshindex'"
						echo "$ucibatch" | uci batch
						changed=1
						syslogmessage="Setting mesh interface name to [ mesh$meshindex ]"
						write_to_syslog
					fi
					meshindex=$(($meshindex+1))
				done

				if [ "$changed" -eq 1 ]; then
					changed=0
					restart_mesh
					continue
				fi

				# get a list of interfaces
				get_mesh_iflist

				for iface in $iflist; do
					wait_for_interface "$iface"

					if [ "$ifstatus" = "down" ]; then
						continue
					fi

					debugtype="debug"
					syslogmessage="interface $iface is $ifstatus"
					write_to_syslog

					# get list of mesh parameters for this interface
					get_params

					if [ -z "$params" ]; then
						# this is not a mesh interface
						continue
					else
						# Check if this interface has a uci ifname
						uciname=$(uci show wireless | grep "ifname='$iface'" | awk -F "." '{printf "wireless.%s" $2}')

						if [ -z "$uciname" ]; then
							# Error - No interface name in config, we should have added one
							debugtype="err"
							syslogmessage="Error getting mesh interface name"
							write_to_syslog
							continue
						fi
					fi

					#configure parameters found in wireless config
					check_mesh_params
					#override wireless config and/or set parameters found in mesh11sd config
					uciname="mesh11sd.mesh_params"
					check_mesh_params
					check_mesh_phantom
				done
			else
				debugtype="info"
				syslogmessage="No mesh interfaces detected yet...."
				write_to_syslog
			fi
		fi

		sleep $checkinterval
	done

	exit 0

else
	echo "Unrecognised command - For help, try mesh11sd --help "
fi
