#!/bin/sh
export PATH="/usr/lib/ksvd/bin:/usr/sbin:/usr/bin:/bin:/usr/local/bin:$PATH"
Lock_File="/tmp/pxe_server_script.lock"
Log_File="/var/log/pxe-network.log"
Lease_File="/var/log/dnsmasq.leases"
Dhcp_Log_File="/var/log/dnsmasq.log"
Path_File="/usr/lib/ksvd/bin"
Settings_File="/var/lib/ksvd/pxe_settings.conf"
Tftp_Server_Path="/var/lib/tftpboot"
Ftp_Server_Path="/var/ftp/kylin"
Daemon_Status="/tmp/pxe_server_daemon.flag"
Detect_Interface_Name=""
Pxe_Interface_Name=""
Pxe_Server_Addr=""
Pxe_Server_Netmask=""
Pxe_Server_Dhcp_Range=""
Pxe_Server_Exclude_Addr=""
Pxe_Server_Iso=""
Pxe_Final_Exclude_Range=""
Pxe_Server_Gateway=""
Expanded_Range=""

# 普通日志打印info级别；影响单个服务的打印warn级别；影响ksvd服务启动的打印error级别
function log_(){
	#judge the params,it must 2 params;
	if [ 2 -gt $# ]
	then
		return -1;
	fi
	level=$1
	shift 1
	message=$@
	#判断日志文件是否存在，不存在则创建
	if [ ! -e "${Log_File}" ];then
		touch ${Log_File}
	fi
	#日志时间
	local curtime
	curtime=`date +"%Y-%m-%d %H:%M:%S"`
	log_split_time=`date +"%Y%m%d%H%M%S"`
	#判断日志的大小，然后如果对于指定的行数，则应该备份旧的日志文件，然后创建新的日志文件
	local cursize
	local fsize=200000000
	# 计算现有的日志文件的行数
	cursize=`cat ${Log_File} | wc -c`
	if [ ${fsize} -lt ${cursize} ];then
		# 备份文件名为：日期.log
		mv ${Log_File} /var/log/"pxe-network-"${log_split_time}".log"
		# 创建新的日志文件
		touch ${Log_File}
	fi
	# 打印控制台日志和记录日志到文件
	echo "[${curtime}][${level}]${message}[$0]" >>${Log_File}
	[[ ${level} = "fatal" ]] && exit 1
}

function Generate_Generic_Templat() {
Feature_Module="\$feature_all_video_module"
local Grub_Path_File="$1"
log_ "info" "*** generate grub.cfg templat ***"
cat << EOF > ${Grub_Path_File}
set default="0"

function load_video {
	if [ x$Feature_Module = xy ]; then
		insmod all_video
	else
		insmod efi_gop
		insmod efi_uga
		insmod ieee1275_fb
		insmod vbe
		insmod vga
		insmod video_bochs
		insmod video_cirrus
	fi
}

load_video
set gfxpayload=keep
insmod gzio
insmod part_gpt
insmod ext2

set timeout=5
### END /etc/grub.d/00_header ###

search --no-floppy --set=root -l 'KylinSec'
EOF
}
function Generate_Grub_Templat() {
Feature_Module="\$feature_all_video_module"
local vm_tool="$1"
local initrd_tool="$2"
local autoinstall_dir="$3"
log_ "info" "*** generate grub.cfg templat ***"

Generate_Generic_Templat "${Path_File}/grub.cfg-ks-templat"
cat << EOF >> ${Path_File}/grub.cfg-ks-templat

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Install KylinSec(PXE) OS' --class red --class gnu-linux --class gnu --class os {
        echo 'Loading kernel ...'
        root=(tftp,$Pxe_Server_Addr)
        $vm_tool /vmlinuz inst.repo=ftp://$Pxe_Server_Addr/kylin ip=dhcp ro net.ifnames=0 biosdevname=0 inst.ks=ftp://$Pxe_Server_Addr/kylin/kickstart/ks.cfg
        echo 'Loading initrd ...'
        $initrd_tool /initrd.img
}
### END /etc/grub.d/10_linux ###
EOF

Generate_Generic_Templat "${Path_File}/grub.cfg-live-templat"
cat << EOF >> ${Path_File}/grub.cfg-live-templat

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Install KylinSec(PXE) OS' --class red --class gnu-linux --class gnu --class os {
        echo 'Loading kernel ...'
        $vm_tool /vmlinuz ip=dhcp ro net.ifnames=0 biosdevname=0  video=efifb:off console=tty0 root=live:ftp://$Pxe_Server_Addr/kylin/LiveOS/squashfs.img rd.live.image
        echo 'Loading initrd ...'
        $initrd_tool /initrd.img
}
### END /etc/grub.d/10_linux ###
EOF

Generate_Generic_Templat "${Path_File}/grub.cfg-tci-templat"
cat << EOF >> ${Path_File}/grub.cfg-tci-templat

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Install KylinSec(PXE) OS' --class red --class gnu-linux --class gnu --class os {
        root=(tftp,$Pxe_Server_Addr)
        echo 'Loading kernel ...'
        $vm_tool /vmlinuz rdinit=/sbin/init console=tty0,115200 quiet autoinstall=ftp://$Pxe_Server_Addr/kylin/$autoinstall_dir
        echo 'Loading initrd ...'
        $initrd_tool /initrd.img
}
### END /etc/grub.d/10_linux ###
EOF

Generate_Generic_Templat "${Path_File}/grub.cfg-repo-templat"
cat << EOF >> ${Path_File}/grub.cfg-repo-templat

### BEGIN /etc/grub.d/10_linux ###
menuentry 'Install KylinSec(PXE) OS' --class red --class gnu-linux --class gnu --class os {
        echo 'Loading kernel ...'
        $vm_tool /vmlinuz ip=dhcp ro net.ifnames=0 biosdevname=0 console=tty0 inst.repo=ftp://$Pxe_Server_Addr/kylin
        echo 'Loading initrd ...'
        $initrd_tool /initrd.img
}
### END /etc/grub.d/10_linux ###
EOF

}

function Generate_Pxe_Templat() {
log_ "info" "*** generate dnsmasq.conf templat ***"
cat << EOF > ${Path_File}/dnsmasq.conf-templat
port=0
except-interface=lo
interface=${Pxe_Interface_Name}
bind-interfaces

dhcp-lease-max=200
dhcp-option=1,$Pxe_Server_Netmask
dhcp-option=3,$Pxe_Server_Gateway

#dhcp-ignore=tag:!known
#dhcp-host=70:b5:e8:37:1c:ed,set:known
#dhcp-host=70:b5:e8:49:7f:98,set:known
#dhcp-host=94:c6:91:b0:38:c3,set:known

enable-tftp
tftp-root=$Tftp_Server_Path
tftp-unique-root
tftp-no-blocksize
tftp-max=200

log-facility=${Dhcp_Log_File} 
dhcp-leasefile=${Lease_File}
EOF
}

# 将 IP 地址转换为十进制数字
function Ip_To_Decimal() {
	local ip="$1"
	IFS='.' read -r -a octets <<< "$ip"
	local octet1=$((octets[0] * 256 * 256 * 256))
	local octet2=$((octets[1] * 256 * 256))
	local octet3=$((octets[2] * 256))
	local octet4=${octets[3]}
	local decimal=$((octet1 + octet2 + octet3 + octet4))
	echo "$decimal"
}

# 将十进制数字转换为 IP 地址
function Decimal_To_Ip() {
	local decimal=$1
	local octet1=$((decimal / 256 / 256 / 256))
	local octet2=$((decimal / 256 / 256 % 256))
	local octet3=$((decimal / 256 % 256))
	local octet4=$((decimal % 256))
	local ip="$octet1.$octet2.$octet3.$octet4"
	echo "$ip"
}

function Is_Valid_Ip() {
	local ip=$1
	local stat=1

	if [[ $ip =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
		IFS='.' read -ra octets <<< "$ip"
		if [[ ${#octets[@]} -eq 4 ]]; then
			stat=0
			for octet in "${octets[@]}"; do
				if [[ $octet -lt 0 || $octet -gt 255 ]]; then
					stat=1
					break
				fi
			done
		fi
	fi

	return $stat
}

function Update_Config() {
	local key=$1
	local value=$2

	# 使用 sed 命令替换配置文件中指定的键值对
	sed -i "s/^$key=.*/$key=\"$value\"/" "$Settings_File"
}

function Check_Config() {
	if [ ! -e "$Pxe_Server_Iso" ]; then
		log_ "warn" "ISO: $Pxe_Server_Iso is not exist!!!"
		exit 1
	fi

	if [ x"$Pxe_Server_Addr" = x"" ]; then
		log_ "warn" "PXE SERVER ADDR: $Pxe_Server_Addr is null!!!"
		exit 1
	fi

	if [ x"$PXE_SERVER_NETMASK" = x"" ]; then
		log_ "warn" "PXE SERVER NETMASK: $PXE_SERVER_NETMASK is null!!!"
		exit 1
	fi
	
	if [ x"$PXE_SERVER_DHCP_RANGE" = x"" ]; then
		log_ "warn" "PXE SERVER DHCP RANGE: $PXE_SERVER_DHCP_RANGE is null!!!"
		exit 1
	fi
	
	if ! Is_Valid_Ip "$Pxe_Server_Addr"; then
		log_ "info" "Invalid IP address: $Pxe_Server_Addr"
		exit 1
	fi
}

function Read_Config_File() {
	local config_file=$1
    
	if [ ! -e "$config_file" ]; then
		log_ "warn" "file: $config_file is not exist!!!"
		#echo "file: $config_file is not exist!!!"
		exit 1
	fi
	source $config_file  # 通过source命令执行配置文件，将其中的变量赋值到当前环境

	Pxe_Server_Addr="$PXE_SERVER_ADDR"
	Pxe_Server_Netmask="$PXE_SERVER_NETMASK"
	Pxe_Server_Dhcp_Range="$PXE_SERVER_DHCP_RANGE"
	Pxe_Server_Exclude_Addr="$PXE_SERVER_EXCLUDE_ADDR"
	Pxe_Server_Iso="$PXE_SERVER_ISO"
	Pxe_Server_Exclude_Addr=`echo ${Pxe_Server_Exclude_Addr}| tr ',' '\n' | sort -t . -k 1,1n -k 2,2n -k 3,3n -k 4,4n | paste -sd ","`
	log_ "info" "Pxe_Server_Addr: $Pxe_Server_Addr"
	log_ "info" "Pxe_Server_Netmask: $Pxe_Server_Netmask"
	log_ "info" "Pxe_Server_Dhcp_Range: $Pxe_Server_Dhcp_Range"
	log_ "info" "Pxe_Server_Exclude_Addr: $Pxe_Server_Exclude_Addr"
	log_ "info" "Pxe_Server_Iso: $Pxe_Server_Iso"
	   
	Pxe_Server_Gateway=$(ip r | awk '/default/ {print $3}')
	log_ "info" "Pxe_Server_Gateway: $Pxe_Server_Gateway"
}

function Get_Phy_Eth_Name() {
	# 获取范围内的接口列表
	interfaces=$(ls -F /sys/devices/virtual/net/ | grep "/$" | sed 's/\/$//g')
	interface_name=$1

	# 判断接口名称是否在范围内
	if echo "$interfaces" | grep -q -w "$interface_name"; then
		log_ "info" "*** eth:$interface_name is virtual interface ***"
		Detect_Interface_Name=$(ip a | grep "$interface_name" | grep "\@" | awk -F "@" '{print $2}' | awk -F ":" '{print $1}')
		if [ x"$Detect_Interface_Name" != x"" ]; then
			log_ "info" "*** get phy interface: ${Detect_Interface_Name}***"
		else
			Detect_Interface_Name=$1
			log_ "warn" "*** get phy interface is error ,reset Detect_Interface_Name:${Detect_Interface_Name} ***"
		fi
	else
		Detect_Interface_Name=$1
		log_ "info" "*** interface: ${Detect_Interface_Name} is actual interface***"
	fi

	echo "$Detect_Interface_Name"
}

function Get_Interface_For_Addr() {
	ip_address=$1
	# 检查IP地址合法性
	if ! Is_Valid_Ip ${ip_address}; then
		log_ "info" "Invalid IP address: $ip_address"
		exit 1
	fi

	# 找出设备上对应地址的接口名称
	#interface=$(ip route get $ip_address | awk '{if (NR==1) print $NF}')
	Pxe_Interface_Name=$(ip r | grep -w "$ip_address" | awk '{print $3}')
	if [ -z "$Pxe_Interface_Name" ]; then
		ifconfig macvlan0 | grep "$3" >/dev/null 2>&1
		if [ $? != 0 ]; then
			log_ "warn" "No interface found for IP address: $ip_address"
			exit 1
		else
			Pxe_Interface_Name="macvlan0"
			log_ "info" "Interface get for ifconfig address $ip_address: $Pxe_Interface_Name"
		fi
	else
		log_ "info" "Interface for IP address $ip_address: $Pxe_Interface_Name"
	fi
}

function Get_Gateways_Line_Number() {
	awk '/# gateways/{print NR; exit}' /usr/sbin/dhclient-script
}

#在标识# gateways的行下面添加DEFROUTE="no"
function Add_Defroute() {
	local line_number=$(Get_Gateways_Line_Number)
	sed -i "${line_number}aDEFROUTE=\"no\"" /usr/sbin/dhclient-script
	log_ "info" "已在/usr/sbin/dhclient-script文件中添加DEFROUTE=\"no\""
}

#删除文件/usr/sbin/dhclient-script中的一行DEFROUTE="no"
function Remove_Defroute() {
	sed -i '/DEFROUTE="no"/d' /usr/sbin/dhclient-script
	log_ "info" "已删除/usr/sbin/dhclient-script文件中的DEFROUTE=\"no\""
}

function Dhcp_Server_Detect(){
	local interface=$1
	local macvlan_interface="macvlan_test"
	local dhcp_server=""
	local original_gateway=$(ip r | awk '/default/ {print $3}')
	local original_dev=$(ip r | awk '/default/ {print $5}')
	log_ "info" "dhcp servert detect name:$interface"

	ip a | grep "$interface" >/dev/null 2>&1
	if [ $? != 0 ]; then
		log_ "warn" "detect interface is not exist!"
		exit 1
	fi

	local original_interface=$(Get_Phy_Eth_Name "${interface}")
	# 创建 macvlan 接口
	ip link add link $original_interface $macvlan_interface type macvlan >/dev/null 2>&1

	# 激活 macvlan 接口
	ip link set $macvlan_interface up >/dev/null 2>&1

	dhclient -x
	original_pid_list=$(ps aux | grep dhclient | grep -v grep | awk '{print $2}')
	for pid_0 in ${original_pid_list}; do
		if [[ -n "$pid_0" ]]; then
			log_ "info" "Killing process0 $pid_0"
			kill -9 ${pid_0} 2>&1
			sleep 1
		fi
	done

	Add_Defroute
	sleep 1
	rm -rf /var/lib/dhclient/dhclient.leases >/dev/null 2>&1
	# 使用 dhclient 获取 IP 地址
	dhcp_output=$(dhclient -v $macvlan_interface -timeout 12 2>&1)
	# 检查是否成功获取到 IP 地址
	if echo "$dhcp_output" | grep -q "bound to"; then
		# 提取 DHCP 服务器地址
		dhcp_server=$(echo "$dhcp_output" | awk '/DHCPACK from/ {print $3}')
		#echo "成功获取到 IP 地址，DHCP 服务器地址为：$dhcp_server"
		#echo "Detect dhcp server address: $dhcp_server"
		#echo "There is already a DHCP service $dhcp_server in the current network environment"
		echo "$dhcp_server"
		dhclient -x
		current_gateway=$(ip r | awk '/default/ {print $3}')
		current_dev=$(ip r | awk '/default/ {print $5}')
		if [ x"$current_gateway" != x"$original_gateway" ] && [ x"$current_dev" != x"$original_dev" ]; then
			ip -4 route replace default via ${original_gateway} dev ${original_dev} >/dev/null 2>&1
			#ping $original_gateway -c 1 2>&1 1>/dev/null
			IPADDR=$(ifconfig $original_dev | grep -w inet | awk '{ print $2 }')
			/sbin/arping -q -A -c 1 -I ${original_dev} ${IPADDR} 2>&1
			sleep 2
			/sbin/arping -q -U -c 1 -I ${original_dev} ${IPADDR} 2>&1
			log_ "info" "get original_dev:$original_dev, IPADDR:$IPADDR"
		fi
		
		ip link set $macvlan_interface down
		ip link delete dev $macvlan_interface
	else
		dhclient -x
		current_gateway=$(ip r | awk '/default/ {print $3}')
		current_dev=$(ip r | awk '/default/ {print $5}')
		if [ x"$current_gateway" != x"$original_gateway" ] && [ x"$current_dev" != x"$original_dev" ]; then
			ip -4 route replace default via ${original_gateway} dev ${original_dev} >/dev/null 2>&1
			#ping $original_gateway -c 1 2>&1 1>/dev/null
			IPADDR=$(ifconfig $original_dev | grep -w inet | awk '{ print $2 }')
			/sbin/arping -q -A -c 1 -I ${original_dev} ${IPADDR} 2>&1
			sleep 2
			/sbin/arping -q -U -c 1 -I ${original_dev} ${IPADDR} 2>&1
			log_ "info" "get original_dev:$original_dev, IPADDR:$IPADDR"
		fi
		ip link set $macvlan_interface down >/dev/null 2>&1
		ip link delete dev $macvlan_interface >/dev/null 2>&1
		#echo "No DHCP reply received."
	fi

	pid_list=$(ps aux | grep dhclient | grep macvlan_test | awk '{print $2}')
	for pid in ${pid_list}; do
		if [[ -n "$pid" ]]; then
			log_ "info" "Killing process1 $pid"
			kill -9 ${pid} 2>&1
			sleep 1
		fi
	done

	Remove_Defroute
	if [ x"$dhcp_server" != x"" ]; then
		log_ "info" "DHCP server address: $dhcp_server"
		return 1
	else
		log_ "info" "No DHCP reply received."
		return 0
	fi

}

function Dhcp_Server_Detect_Remove(){
	local macvlan_interface="macvlan_test"
	dhclient -x
	local original_pid_list=$(ps aux | grep dhclient | grep ${macvlan_interface} | grep -v grep | awk '{print $2}')
	for pid_0 in ${original_pid_list}; do
		if [[ -n "$pid_0" ]]; then
			log_ "info" "Dhcp_Server_Detect_Remove Killed  process2 $pid_0"
			kill -9 ${pid_0} 2>&1
			sleep 1
		fi
	done
	
	ifconfig "${macvlan_interface}" 1>/dev/null 2>&1
	if [ $? == 0 ]; then
		ip link set $macvlan_interface down >/dev/null 2>&1
		ip link delete dev $macvlan_interface >/dev/null 2>&1
	fi
	
	Remove_Defroute
}

function Send_Range_To_Dnsmasq() {
	local line_number=$(awk '/dhcp-lease-max/{print NR; exit}' ${Path_File}/dnsmasq.conf-templat)
	local decimal_min=""
	local decimal_max=""
	local pxe_final_range=$1
	#sed -i '/dhcp-range=/d' ${Path_File}/dnsmasq.conf-templat

	log_ "info" "send range to Dnsmasq line: $line_number"

	IFS=',' read -ra exclude_range <<< "$pxe_final_range"
	for exclude_ip in "${exclude_range[@]}"; do
		IFS='-' read -ra final_range <<< "$exclude_ip"
		decimal_min=$(Decimal_To_Ip "${final_range[0]}")
		decimal_max=$(Decimal_To_Ip "${final_range[1]}")
		log_ "info" "Decimal number converted to IP address: $decimal_min-$decimal_max"
		local exclude_start_ip="${decimal_min}"
		local exclude_end_ip="${decimal_max}"
		sed -i "${line_number}adhcp-range=set\:red\,$exclude_start_ip\,$exclude_end_ip,2h"  ${Path_File}/dnsmasq.conf-templat
		
		log_ "info" "send exclude range: $exclude_start_ip-$exclude_end_ip"
	done
}

# 定义函数来过滤 DHCP 范围
function Filter_Dhcp_Range() {
	local dhcp_range="$1"
	local exclude_range=""
	local decimal_min=""
	local decimal_max=""

	exclude_range=${Expanded_Range}
	# 将 DHCP 范围字符串按照 "-" 分割为数组
	IFS='-' read -ra range <<< "$dhcp_range"
	local start_ip="${range[0]}"
	local end_ip="${range[1]}"

	local filtered_ranges=()
	log_ "info" "filter dhcp range start_ip-end_ip : $start_ip-$end_ip"
	#local Pxe_Server_Subnet="10.30.3"
	# 处理排除范围的情况
	if [[ ${exclude_range} =~ ^[0-9]+\-[0-9]+$ ]]; then
		log_ "info" "The x-y ip range exclude : $exclude_range"
		# 排除范围是形如 "x-y" 的字符串
		IFS='-' read -ra exclude_range_arr <<< "$exclude_range"
		local exclude_start="${exclude_range_arr[0]}"
		local exclude_end="${exclude_range_arr[1]}"

		if [[ ${exclude_start} -gt ${start_ip} && ${exclude_start} -lt ${end_ip} ]]; then
			filtered_ranges+=("$start_ip-$(($exclude_start - 1))")
		fi

		if [[ ${exclude_end} -gt ${start_ip} && ${exclude_end} -lt ${end_ip} ]]; then
			filtered_ranges+=("$(($exclude_end + 1))-$end_ip")
		fi
	elif [[ ${exclude_range} =~ ^[0-9]+\~[0-9]+$ ]]; then
		log_ "info" "The x~y ip range exclude : $exclude_range"
		# 排除范围是形如 "x~y" 的字符串
		IFS='~' read -ra exclude_range_arr <<< "$exclude_range"
		local exclude_start="${exclude_range_arr[0]}"
		local exclude_end="${exclude_range_arr[1]}"

		if [[ ${exclude_start} -gt ${start_ip} && ${exclude_start} -lt ${end_ip} ]]; then
			filtered_ranges+=("$start_ip-$(($exclude_start - 1))")
		fi

		if [[ ${exclude_end} -gt ${start_ip} && ${exclude_end} -lt ${end_ip} ]]; then
			filtered_ranges+=("$(($exclude_end + 1))-$end_ip")
		fi
	else
		log_ "info" "The sigle ip exclude : $exclude_range"
		# 排除范围是单个 IP 地址
		IFS=',' read -ra exclude_ips <<< "$exclude_range"
		local cur_start=${start_ip}
		local cur_end=${end_ip}

		for ip in "${exclude_ips[@]}"; do
			if [[ ${ip} -ge ${start_ip} && ${ip} -le ${end_ip} ]]; then
				cur_end=$((${ip} - 1))
				if [[ ${cur_start} -lt ${cur_end} ]]; then
					filtered_ranges+=("$cur_start-$cur_end")
				fi
				cur_start=$((${ip} + 1))
			fi
		done

		if [[ ${cur_start} -lt ${end_ip} ]]; then
			filtered_ranges+=("$cur_start-$end_ip")
		fi
	fi

	# 输出过滤后的 DHCP 范围
	#printf '过滤后的范围: %s\n' "${filtered_ranges[*]}"
	Pxe_Final_Exclude_Range=""
	for range_f in "${filtered_ranges[@]}"; do
		if [[ ! -z ${Pxe_Final_Exclude_Range} ]]; then
			Pxe_Final_Exclude_Range+=","
		fi
		Pxe_Final_Exclude_Range+="$range_f"
	done

	log_ "info" "get final exclude range:$Pxe_Final_Exclude_Range"
	Send_Range_To_Dnsmasq "$Pxe_Final_Exclude_Range"
}

function Expand_Range() {
	local input=$Pxe_Server_Exclude_Addr
	local result=""
	local decimal=""
	local decimal_min=""
	local decimal_max=""
	local dhcp_range=$1

	log_ "info" "Expand_Range exclude addr: $input, dhcp range:$dhcp_range"

	if [ x"$input" = x"" ]; then
		log_ "info" "Pxe_Server_Exclude_Addr is not setting"
		Send_Range_To_Dnsmasq "$dhcp_range"
		return 1
	fi

	Expanded_Range=""
	IFS=',' read -ra ranges <<< "$input"  # 根据逗号分割字符串
	for range in "${ranges[@]}"; do
		if [[ $range =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)-([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then  # 处理连字符分隔的范围
			decimal_min=$(Ip_To_Decimal "${BASH_REMATCH[1]}")
			decimal_max=$(Ip_To_Decimal "${BASH_REMATCH[2]}")
			log_ "info" "IP address converted to decimal: $decimal_min-$decimal_max"
			for ((i=${decimal_min}; i<=${decimal_max}; i++)); do
				Expanded_Range+=",$i"
			done
		elif [[ $range =~ ^([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)~([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$ ]]; then  # 处理~
			decimal_min=$(Ip_To_Decimal "${BASH_REMATCH[1]}")
			decimal_max=$(Ip_To_Decimal "${BASH_REMATCH[2]}")
			log_ "info" "IP address converted to decimal: $decimal_min-$decimal_max"
			for ((i=${decimal_min}; i<=${decimal_max}; i++)); do
				Expanded_Range+=",$i"
			done
		else
			decimal=$(Ip_To_Decimal "$range")
			log_ "info" "IP address converted to decimal: $decimal"
			Expanded_Range+=",$decimal"
		fi
	done

	Expanded_Range=${Expanded_Range:1}  # 移除开头多余的逗号
	log_ "info" "Get Expand_Range $Expanded_Range"
    
	Filter_Dhcp_Range "$dhcp_range"
}

function Auto_Get_Dhcp_Server_Range() {
	#local net_mask="255.255.255.0"
	local net_mask="$Pxe_Server_Netmask"
    
	log_ "info" "get dhcp server netmask: $net_mask"
	ip_decimal=$(echo $Pxe_Server_Addr | awk -F '.' '{print ($1*(2^24)+$2*(2^16)+$3*(2^8)+$4)}')
	mask_decimal=$(echo $net_mask | awk -F '.' '{print ($1*(2^24)+$2*(2^16)+$3*(2^8)+$4)}')

	# 计算网络地址和广播地址
	network_decimal=$((ip_decimal & mask_decimal))
	broadcast_decimal=$((ip_decimal | (~mask_decimal & 0xffffffff)))

	# 计算 DHCP 范围起始和结束的十进制数
	range_start_decimal=$((network_decimal + 2))
	range_end_decimal=$((broadcast_decimal - 1))

	# 将起始和结束的十进制数转换为 IP 地址格式
	#Pxe_Range_Start_Ip=$(echo $range_start_decimal | awk '{printf("%d.%d.%d.%d\n", and(int($1/(2^24)),255), and(int($1/(2^16)),255), and(int($1/(2^8)),255), and($1,255))}')
	#Pxe_Range_End_Ip=$(echo $range_end_decimal | awk '{printf("%d.%d.%d.%d\n", and(int($1/(2^24)),255), and(int($1/(2^16)),255), and(int($1/(2^8)),255), and($1,255))}')

	log_ "info" "DHCP range: $range_start_decimal - $range_end_decimal"
	Expand_Range "$range_start_decimal-$range_end_decimal"
}

function Get_Pxe_Dhcp_Range() {
	local input=$PXE_SERVER_DHCP_RANGE
	log_ "info" "Get Pxe SERVER DHCP range : $input"
	IFS=',' read -ra ranges <<< "$input" 
	for range in "${ranges[@]}"; do
		if [[ "$range" = *"~"* ]]; then
			log_ "info" "set multi-segment dhcp range : $range"
			IFS='~' read -ra final_range <<< "$range"
			local decimal_min=$(Ip_To_Decimal "${final_range[0]}")
			local decimal_max=$(Ip_To_Decimal "${final_range[1]}")
			log_ "info" "Ip converted to Decimal number: $decimal_min-$decimal_max"
			local exclude_start_ip="${decimal_min}"
			local exclude_end_ip="${decimal_max}"
			Expand_Range "$exclude_start_ip-$exclude_end_ip"
		else
			log_ "info" "not set segment dhcp range : $range"
			Auto_Get_Dhcp_Server_Range
		fi
	done
}

function Send_Dhcp_Boot_To_Dnsmasq() {
	local boot_file=$1
	local line_number=$(awk '/tftp-max/{print NR; exit}' ${Path_File}/dnsmasq.conf-templat)

	#sed -i '/dhcp-boot=/d' ${Path_File}/dnsmasq.conf-templat

	sed -i "${line_number}adhcp-boot=tag\:red\,$boot_file"  ${Path_File}/dnsmasq.conf-templat

	log_ "info" "send dhcp boot file: $boot_file"
        
}

function Check_Pxe_Server_Status(){
	services=("vsftpd")
	status=()

	#check server status
	for service in "${services[@]}"; do
		if s=$(systemctl is-active "$service.service"); then
			status+=("$s")
			log_ "info" "*** $service service is running ***"
		else
			log_ "warn" "*** $service service is not running ***"
			exit 1
		fi
	done

	ps aux | grep -w dnsmasq | grep "$Tftp_Server_Path" 1>/dev/null 2>/dev/null
	if [ $? != 0 ]; then
		log_ "warn" "*** dnsmasq service is not running ***"
		exit 1
	else
		log_ "info" "*** dnsmasq service is running ***"
	fi

	log_ "info" "PXE Server Startd Successfully"
}

function Start_Pxe_Server() {
	log_ "info" "*** Start configuring the FTP server ***"
	local GRUB_VM_NAME="vmlinuz"
	local GRUB_INIT_NAME="initrd.img"
	local GRUB_AUTO_DIR_NAME=""
	if [ -d "/var/ftp" ]; then
		mkdir -p "$Ftp_Server_Path"
	else
		systemctl status vsftpd.service | grep vsftpd  >/dev/null 2>&1
		if [ $? != 0 ]; then
			log_ "warn" "Failed: ftp server is not install, please check ..."
			exit 1
		else
			mkdir -p /var/ftp
			mkdir -p "$Ftp_Server_Path"
		fi
	fi
	log_ "info" "*** Start mount iso ... ***"
	local mount_name=$(mount | grep "$Ftp_Server_Path" | awk '{print $1}')
	if [ x"$mount_name" != x"$Pxe_Server_Iso" ]; then
		umount "$Ftp_Server_Path" >/dev/null 2>&1
		mount -o loop ${Pxe_Server_Iso} "$Ftp_Server_Path" >/dev/null 2>&1
		if [ $? != 0 ]; then
			log_ "warn" "Failed: mount iso error,stop config pxe server"
			exit 1
		fi
	fi

	log_ "info" "*** Start configuring the TFTP server ***"
	if [ ! -d "$Tftp_Server_Path" ]; then
		mkdir -p "$Tftp_Server_Path"
		log_ "warn" "tftp server is maybe not install, please check ..."
	fi
	#cp -f ${Path_File}/tftp-templat /etc/xinetd.d/tftp
	GRUB_CFG_PATH=$(find /var/ftp/kylin/EFI -name "grub.cfg" -type f 2>/dev/null | head -n 1)
	if [ -z "$GRUB_CFG_PATH" ]; then
		log_ "info" "GRUB_CFG_PATH is null from /var/ftp/kylin/EFI, please again ..."
		GRUB_CFG_PATH=$(find /var/ftp/kylin/ -name "grub.cfg" -type f 2>/dev/null | head -n 1)
	fi
	log_ "info" "GRUB_CFG_PATH is $GRUB_CFG_PATH"
	if [ -f "${Ftp_Server_Path}/images/pxeboot/vmlinuz" ]; then
		cp -f ${Ftp_Server_Path}/images/pxeboot/vmlinuz ${Tftp_Server_Path}/vmlinuz
		cp -f ${Ftp_Server_Path}/images/pxeboot/initrd.img ${Tftp_Server_Path}/initrd.img
		GRUB_VM_NAME="/images/pxeboot/vmlinuz"
		GRUB_INIT_NAME="/images/pxeboot/initrd.img"
	elif [ -f "${Ftp_Server_Path}/LiveOS/vmlinuz0" ]; then
		cp -f ${Ftp_Server_Path}/LiveOS/vmlinuz0 ${Tftp_Server_Path}/vmlinuz
		cp -f ${Ftp_Server_Path}/LiveOS/initrd0.img ${Tftp_Server_Path}/initrd.img
		GRUB_VM_NAME="/LiveOS/vmlinuz0"
		GRUB_INIT_NAME="/LiveOS/initrd0.img"
	else
		GRUB_VM_NAME=$(cat $GRUB_CFG_PATH | grep autoinstall | awk '{print $2}')
		if [[ -n "$GRUB_VM_NAME" ]]; then
			GRUB_INIT_NAME=$(cat $GRUB_CFG_PATH | grep initrd | head -n 1 | awk '{print $2}')
			GRUB_AUTO_DIR_NAME=$(cat $GRUB_CFG_PATH | sed -n 's/.*autoinstall=\([^ ]*\).*/\1/p')
			cp -f ${Ftp_Server_Path}/${GRUB_VM_NAME} ${Tftp_Server_Path}/vmlinuz
			cp -f ${Ftp_Server_Path}/${GRUB_INIT_NAME} ${Tftp_Server_Path}/initrd.img
		else
			echo "This image does not support PXE installation,vmlinuz or image is not exist."
			umount "$Ftp_Server_Path" >/dev/null 2>&1
			exit 1
		fi
	fi

	GRUB_VM_TOOL=$(grep -w "$GRUB_VM_NAME" "$GRUB_CFG_PATH" |awk '{print $1}' | head -n 1)
	GRUB_INITRD_TOOL=$(grep -w "$GRUB_INIT_NAME" "$GRUB_CFG_PATH" |awk '{print $1}' | head -n 1)
	log_ "info" "Get grub vmlinuz:$GRUB_VM_TOOL $GRUB_VM_NAME, initrd:$GRUB_INITRD_TOOL $GRUB_INIT_NAME"
	Generate_Grub_Templat "$GRUB_VM_TOOL" "$GRUB_INITRD_TOOL" "$GRUB_AUTO_DIR_NAME"

	if [ -f "$Ftp_Server_Path/kickstart/ks.cfg" ]; then
		log_ "info" "start ks grub install ***"
		cp -f ${Path_File}/grub.cfg-ks-templat ${Tftp_Server_Path}/grub.cfg
	elif [ x"/LiveOS/vmlinuz0" = x"$GRUB_VM_NAME" ]; then
		log_ "info" "start live grub install ***"
		cp -f ${Path_File}/grub.cfg-live-templat ${Tftp_Server_Path}/grub.cfg
	elif [[ -n "$GRUB_AUTO_DIR_NAME" ]]; then
		log_ "info" "start tci grub install ***"
		cp -f ${Path_File}/grub.cfg-tci-templat ${Tftp_Server_Path}/grub.cfg
	else
		log_ "info" "start repo grub install ***"
		cp -f ${Path_File}/grub.cfg-repo-templat ${Tftp_Server_Path}/grub.cfg
	fi

	cp -f ${Tftp_Server_Path}/grub.cfg ${Tftp_Server_Path}/BOOT.conf
	mkdir -p ${Tftp_Server_Path}/EFI/BOOT
	cp -f ${Tftp_Server_Path}/grub.cfg ${Tftp_Server_Path}/EFI/BOOT/
	cp -f ${Tftp_Server_Path}/BOOT.conf  ${Tftp_Server_Path}/EFI/BOOT/
	Efi_List=$(ls $Ftp_Server_Path/EFI/BOOT/ | grep -i efi | grep 64)
	for efi_name in ${Efi_List}; do
		if [[ -n "$efi_name" ]]; then
			log_ "info" "Get efi name $efi_name"
			cp -f ${Ftp_Server_Path}/EFI/BOOT/${efi_name} ${Tftp_Server_Path}
			Send_Dhcp_Boot_To_Dnsmasq "$efi_name"
		fi
	done
	cp -f ${Path_File}/dnsmasq.conf-templat ${Tftp_Server_Path}/dnsmasq.conf

	chmod -R 777 ${Tftp_Server_Path}/*
    
	log_ "info" "*** Start configuring the PXE server ***"
	firewall-cmd --add-service=ftp --zone=public 1>/dev/null 2>/dev/null
	firewall-cmd --add-service=tftp --zone=public 1>/dev/null 2>/dev/null
	firewall-cmd --add-service=dhcp --zone=public 1>/dev/null 2>/dev/null

	dnsmasq_pid=`ps aux | grep -w dnsmasq | grep "$Tftp_Server_Path" | awk '{ print $2 }'`
	if [ x"${dnsmasq_pid}" == x"" ];then
		dnsmasq -C ${Tftp_Server_Path}/dnsmasq.conf -u root
	else
		log_ "info" "*** dnsmasq running pid is ${dnsmasq_pid} ***"
		kill -9 ${dnsmasq_pid} 1>/dev/null 2>/dev/null
		sleep 2
		dnsmasq -C ${Tftp_Server_Path}/dnsmasq.conf -u root
	fi
	systemctl restart vsftpd.service >/dev/null 2>&1
	chmod 777 ${Dhcp_Log_File}
}

function Start_() {
	Check_Config
	Get_Interface_For_Addr "${Pxe_Server_Addr}"
        
	#first detect dhcp server
	Dhcp_Server_Detect "${Pxe_Interface_Name}"
	if [ $? == 1 ]; then
		log_ "warn" "There is already a DHCP service in the current network environment."
		exit 2
	fi
    
	Generate_Pxe_Templat
    
	#add exclude dhcp range
	Get_Pxe_Dhcp_Range
        
	Start_Pxe_Server
	Check_Pxe_Server_Status
}

function Stop_(){
	mount | grep "$Ftp_Server_Path" 1>/dev/null 2>/dev/null
	if [ $? = 0 ]; then
		log_ "info" "*** stop [umount /var/ftp/kylin ...] ***"
		umount "$Ftp_Server_Path" 1>/dev/null 2>/dev/null
	fi
    
	rm -rf ${Path_File}/grub.cfg-ks-templat 1>/dev/null 2>/dev/null
	rm -rf ${Path_File}/grub.cfg-repo-templat 1>/dev/null 2>/dev/null
	rm -rf ${Path_File}/dnsmasq.conf-templat 1>/dev/null 2>/dev/null
	rm -rf ${Tftp_Server_Path}/* 1>/dev/null 2>/dev/null
	rm -rf ${Ftp_Server_Path}/* 1>/dev/null 2>/dev/null
	log_ "info" "*** stop [remove file.service ...] ***"

	systemctl stop vsftpd.service >/dev/null 2>&1
	log_ "info" "*** stop [stop vsftpd.service ...] ***"

	dnsmasq_pid=`ps aux | grep -w dnsmasq | grep "$Tftp_Server_Path" | awk '{ print $2 }'`
	if [ x"${dnsmasq_pid}" != x"" ];then
		log_ "info" "*** stop [dnsmasq running pid is ${dnsmasq_pid}] ***"
		kill -9 "$dnsmasq_pid" 1>/dev/null 2>/dev/null
		log_ "info" "*** stop [stop dnsmasq.service ...] ***"
		sleep 1
	fi

	firewall-cmd --remove-service=ftp --zone=public 1>/dev/null 2>/dev/null
	firewall-cmd --remove-service=tftp --zone=public 1>/dev/null 2>/dev/null
}

function Check_Status_For_Dhcp() {
	count=0
	while read -r line; do
		count=$((count+1))
		ip=$(echo "$line" | awk '{print $1}')
		log_ "info" "Start ping -c3 -i0.5 -W1 $ip"
		# 执行 ping 操作并获取返回状态
		ping -c3 -i0.5 -W1 "$ip" >/dev/null 2>&1
		if [ $? -eq 0 ]; then
			printf "%-4s %-5s %3s\n" "$count" "alive" "$line"
		else
			printf "%-4s %-5s %3s\n" "$count" "dead" "$line"
		fi
	done
}

function Add_Status_For_Dhcp() {
	local count=0
	local stat=0
	Status_Show_ >/dev/null 2>&1
	stat=$?
    
	while read -r line; do
		count=$((count+1))
		time_tmp=$(echo "$line" | awk '{print $3}')
		new_time_tmp=$(($time_tmp - 7200))
		date_tmp=$(date -d @"${new_time_tmp}" '+%Y.%m.%d-%H:%M:%S')
		line_tmp=$(echo "$line" | awk '{print $1,$2}')
		printf "%-3s %-3s %-5s %3s\n" "$count" "$stat" "$line_tmp" "$date_tmp"
	done
}

function Dhcp_Offer_Addr_Show() {
	if [ -e "$Lease_File" ]; then
		cat "$Lease_File" | awk '!a[$3]++' | awk '{print $3, $2, $1}' | sort -k3 | Add_Status_For_Dhcp
	fi
}

function Check_Dhcp_Offer_Addr_Alive() {
	if [ -e "$Dhcp_Log_File" ]; then
		cat "$Dhcp_Log_File" | grep DHCPACK | awk '!a[$6]++' | awk '{print $6, $7, $3}' | sort -k3 | Check_Status_For_Dhcp
	fi
}

function Status_Show_(){
	services=("vsftpd" "xinetd" "tftp")
	status=0

	if [ -f "${Daemon_Status}" ];then
		while read -r line; do
			echo "${line}"
			return 2
		done < ${Daemon_Status}
	fi
	#check server status
	for service in "${services[@]}"; do
		if s=$(systemctl is-active "$service.service"); then
			status+=("$s")
			#log_ "info" "*** $service service is running ***"
		else
			#log_ "info" "*** $service service is not running ***"
			if [ x"$service" = x"vsftpd" ]; then
				status=$((${status} + 1))
			fi
		fi
	done

	ps aux | grep -w dnsmasq | grep "$Tftp_Server_Path" 1>/dev/null 2>/dev/null
	if [ $? != 0 ]; then
		#log_ "warn" "*** dnsmasq service is not running ***"
		status=$((${status} + 1))
	fi
    
	mount | grep "$Ftp_Server_Path" >/dev/null 2>&1
	if [ $? != 0 ]; then
		#log_ "info" "*** The iso is failed mounted ***"
		status=$((${status} + 1))
	fi
    
	if [ ${status} -eq 0 ]; then
		echo "active"
		return 0
	else
		echo "inactive"
		return 1
	fi
}

function Get_Lock_Pid() {
    local lock_pid
    if [[ -f "$Lock_File" ]]; then
        lock_pid=$(cat "$Lock_File")
        echo "$lock_pid"
    fi
}

#check arg
if [ "$#" -eq 1 ] && [ "$1" = "stop" ]; then
	if ( set -o noclobber; echo "$$" > "$Lock_File") 2> /dev/null; then
		trap 'rm -f "$Lock_File";rm -f "$Daemon_Status"' INT TERM EXIT

		log_ "info" "Executing stop process."
		echo "stop_running" > ${Daemon_Status}
		Stop_

		# 清理并释放文件锁
		rm -f "$Lock_File"
		rm -f "$Daemon_Status"
		trap - INT TERM EXIT
	else
		# 获取文件锁失败，表示脚本正在后台运行
		echo "The script is currently running,please try again later!"
		exit 3
	fi
	exit 0
elif [ "$#" -eq 1 ] && [ "$1" = "clear" ]; then
	if ( set -o noclobber; echo "$$" > "$Lock_File") 2> /dev/null; then
		trap 'rm -f "$Lock_File";rm -f "$Daemon_Status"' INT TERM EXIT

		log_ "info" "Executing clear process."
		echo "clear_running" > ${Daemon_Status}
		Stop_

		sleep 1
		rm -rf /var/log/dnsmasq.log >/dev/null 2>&1
		rm -rf /var/log/dnsmasq.leases 1>/dev/null >/dev/null 2>&1
        
		Update_Config "PXE_SERVER_ADDR" ""
		Update_Config "PXE_SERVER_NETMASK" ""
		Update_Config "PXE_SERVER_DHCP_RANGE" ""
		Update_Config "PXE_SERVER_EXCLUDE_ADDR" ""
		Update_Config "PXE_SERVER_ISO" ""
        
		# 清理并释放文件锁
		rm -f "$Lock_File"
		rm -f "$Daemon_Status"
		trap - INT TERM EXIT
	else
		# 获取文件锁失败，表示脚本正在后台运行
		echo "The script is currently running,please try again later!"
		exit 3
	fi
	exit 0
elif [ "$#" -eq 1 ] && [ "$1" = "start" ]; then
	# 尝试获取文件锁
	if ( set -o noclobber; echo "$$" > "$Lock_File") 2> /dev/null; then
		# 成功获得文件锁，则进行后续操作
		trap 'rm -f "$Lock_File";rm -f "$Daemon_Status"' INT TERM EXIT

		log_ "info" "Executing start process."
		Status_Show_ >/dev/null 2>&1
		if [ $? == 0 ]; then
			echo "Pxe Server is running..."
			exit 3
		fi
		echo "start_running" > ${Daemon_Status}
		#Stop_
		Read_Config_File ${Settings_File}
		Start_

		# 清理并释放文件锁
		rm -f "$Lock_File"
		rm -f "$Daemon_Status"
		trap - INT TERM EXIT
	else
		# 获取文件锁失败，表示脚本正在后台运行
		echo "The script is currently running,please try again later!"
		exit 3
	fi
	exit 0
elif [ "$#" -eq 1 ] && [ "$1" = "detect" ]; then
	if ( set -o noclobber; echo "$$" > "$Lock_File") 2> /dev/null; then
		trap 'rm -f "$Lock_File"' INT TERM EXIT

		log_ "info" "Executing dhcp detect eth process."
		Read_Config_File ${Settings_File}
		Get_Interface_For_Addr "${Pxe_Server_Addr}"
		Dhcp_Server_Detect ${Pxe_Interface_Name}

		# 清理并释放文件锁
		rm -f "$Lock_File"
		trap - INT TERM EXIT
	else
		# 获取文件锁失败，表示脚本正在后台运行
		echo "The script is currently running,please try again later!"
		exit 3
	fi
	exit 0
elif [ "$#" -eq 2 ] && [ "$1" = "detect" ]; then
	if ( set -o noclobber; echo "$$" > "$Lock_File") 2> /dev/null; then
		trap 'rm -f "$Lock_File"' INT TERM EXIT

		log_ "info" "Executing dhcp detect process."
		Dhcp_Server_Detect "$2"

		# 清理并释放文件锁
		rm -f "$Lock_File"
		trap - INT TERM EXIT
	else
		# 获取文件锁失败，表示脚本正在后台运行
		echo "The script is currently running,please try again later!"
		exit 3
	fi
	exit 0
elif [ "$#" -eq 1 ] && [ "$1" = "show" ]; then
	#log_ "info" "Executing dhcp offer addr show process."
	Dhcp_Offer_Addr_Show
	exit 0
elif [ "$#" -eq 1 ] && [ "$1" = "check_dhcp_offer_addr_alive" ]; then
	if ( set -o noclobber; echo "$$" > "$Lock_File") 2> /dev/null; then
		trap 'rm -f "$Lock_File"' INT TERM EXIT

		log_ "info" "Executing dhcp offer addr alive show process."
		Check_Dhcp_Offer_Addr_Alive

		# 清理并释放文件锁
		rm -f "$Lock_File"
		trap - INT TERM EXIT
	else
		# 获取文件锁失败，表示脚本正在后台运行
		echo "The script is currently running,please try again later!"
		exit 3
	fi
	exit 0
elif [ "$#" -eq 1 ] && [ "$1" = "status" ]; then
	#log_ "info" "Executing show process."
	Status_Show_
	exit 0
else
	#echo "Usage: $0 /path/name.iso ETH_NAME"
	echo "Usage: $0 start"
	echo "Usage: $0 stop"
	echo "Usage: $0 clear"
	echo "Usage: $0 detect [eth]"
	echo "Usage: $0 show"
	echo "Usage: $0 check_dhcp_offer_addr_alive"
	echo "Usage: $0 status"
	#echo "Example: $0 /home/KylinSec-VOI-3.3-6-8.1.9-1-voi.44859-2307-252153-aarch64.iso macvlan0"
	echo ""
	exit 1
fi

