#!/bin/bash

###
# renameInterfaces.sh
#
# Script to rename network interfaces in Linux.
###

# Function to display warning messages
function warn()
{
	local message="${1}"
	echo "${message}" >&2
}

# Function to display error messages and exit
function error()
{
	warn "${@}"
	exit 1
}

# Directory and prefix for network link files
networkd_dir="/etc/systemd/network"
networkd_link_file_prefix="${networkd_dir}/020_interface"

# Function to clean network link files
function cleanNetworkdLinkFiles()
{
	rm -rf "${networkd_link_file_prefix}"*
}

# Function to create network link file
function createNetworkdLinkFile()
{
	local mac="${1}"
	local name="${2}"
	local alias="${3}"

	if [[ -z "${mac}" ]]
	then
		error "Missing required argument: mac"
	elif [[ -z "${name}" ]]
	then
		error "Missing required argument: name"
	elif [[ -z "${alias}" ]]
	then
		warn "Missing recommended argument: alias"
	fi

	local file="${networkd_link_file_prefix}_${name}.link"

	cat > "${file}" <<-EOF
[Match]
Type = ether
MACAddress = ${mac}

[Link]
NamePolicy = 
Name = ${name}
Alias = ${alias}
EOF

	chown root:systemd-network "${file}"
	chmod 640 "${file}"
}

interface_label_eth=1
interface_label_sfp=1
generated_interface_name=

function getInterfaceName()
{
	local interface_type="${1}"

	interface_label_type="interface_label_${interface_type}"
	interface_label="${!interface_label_type}"

	printf -v generated_interface_name "%s%02d" "${interface_type}" "${interface_label}"

	eval "${interface_label_type}=$(( interface_label + 1 ))"
	# declare -g "${interface_label_type}=$(( interface_label + 1 ))"
	# printf -v "${interface_label_type}" "%d" "$(( interface_label + 1 ))"
	# ((interface_label_"${interface_type}"++))
}

interface_label_external_prefix=
interface_label_external=0
generated_interface_alias=

function getInterfaceAlias()
{
	local interface_type="${1}"

	generated_interface_alias=

	if [[ -n "${interface_label_external_prefix}" ]]
	then
		generated_interface_alias="${interface_label_external_prefix} "
	fi

	generated_interface_alias+="Port ${interface_label_external} "

	case "${interface_type}" in
		"eth")
			generated_interface_alias+="RJ45"
			;;
		"sfp")
			generated_interface_alias+="SFP"
			;;
	esac

	interface_label_external=$(( interface_label_external + 1 ))
}

# Associative array to store interface-MAC address pairs
declare -A interface_macs

# Array to maintain the order of interfaces
interface_order=()

# Associative array to store network driver, provides uniqueness
declare -A interface_drivers

# Read interface-MAC pairs and populate arrays
while read -r interface mac
do
	if ! driver=$(ethtool -i "${interface}" 2> /dev/null | grep "driver:")
	then
		warn "Ethtool failed: ${interface}"
		continue
	fi

	case "${driver,,}" in
		*"team"|\
		*"tun"|\
		*"vlan"*|\
		*"vrf")
			warn "Unsupported driver: ${interface} -> ${driver}"
			continue
	esac

	driver=$(echo "${driver}" | cut -d ' ' -f 2-)
	interface_drivers["${driver}"]=0

	interface_macs["${interface}"]="${mac}"
	interface_order+=("${interface}")
done < <(ip -br link | grep -v -w lo | grep -v @ | sort -k 3 | expand | tr -s ' ' | cut -d ' ' -f 1,3)

read -rp "Do you want to clear the existing networkd link files? [Y/n] "
if [[ -z "${REPLY}" || "${REPLY,}" == "y" ]]
then
	cleanNetworkdLinkFiles
fi

read -rp "What is the port number on the label of the first interface? [${interface_label_external}] "
if [[ -n "${REPLY}" ]]
then
	interface_label_external="${REPLY}"
fi

# Process interfaces
for interface in "${interface_order[@]}"
do
	if ! ethtool_data=$(ethtool "${interface}" 2> /dev/null)
	then
		warn "Ethtool failed: ${interface}"
		continue
	fi

	if ! ethtool_connector=$(echo "${ethtool_data}" | grep "Port:")
	then
		warn "Unsupported interface: ${interface}"
		continue
	fi

	case "${ethtool_connector,,}" in
		*"mii"|\
		*"twisted pair")
			interface_type="eth"
			;;
		*"fibre")
			interface_type="sfp"
			;;
	*)
		warn "Unsupported connector: ${ethtool_connector}"
		continue
	esac

	interface_mac=${interface_macs[${interface}]}

	read -rp "Would you like to rename interface '${interface} (${interface_mac})'? [Y/n/x] "
	case "${REPLY}" in
		"X"|"x")
			break
			;;
		"N"|"n")
			continue
			;;
		"Y"|"y"|*)
			;;
	esac

	read -rp "If this interface not onboard and part of a NIC, set a prefix: [${interface_label_external_prefix}] "
	if [[ -n "${REPLY}" ]]
	then
		interface_label_external_prefix="${REPLY}"
	fi

	read -rp "What is the port number on the label of this interface? [${interface_label_external}] "
	if [[ -n "${REPLY}" ]]
	then
		interface_label_external="${REPLY}"
	fi

	getInterfaceName "${interface_type}"
	read -rp "Set the new interface name: [${generated_interface_name}] "
	if [[ -n "${REPLY}" ]]
	then
		generated_interface_name="${REPLY}"
	fi

	getInterfaceAlias "${interface_type}"
	read -rp "Set the new interface alias: [${generated_interface_alias}] "
	if [[ -n "${REPLY}" ]]
	then
		generated_interface_alias="${REPLY}"
	fi

	createNetworkdLinkFile "${interface_mac}" "${generated_interface_name}" "${generated_interface_alias}"
done

read -rp "Do you want to reload the drivers and restart systemd-networkd now? [y/N] "
if [[ -z "${REPLY}" || "${REPLY,,}" == "n" ]]
then
	exit 0
fi

for driver in "${!interface_drivers[@]}"
do
	rmmod "${driver}"
done

systemctl restart systemd-networkd


for driver in "${!interface_drivers[@]}"
do
	modprobe "${driver}"
done
