#!/bin/sh

cachedir="/var/cache/alterator/net-eth"
precommit_hooks_dir=/usr/lib/alterator/hooks/net-eth-precommit.d
postcommit_hooks_dir=/usr/lib/alterator/hooks/net-eth.d

rdelim='[[:space:]]\+'
wdelim=' '

max_hostname_length=64

alterator_api_version=1

. alterator-sh-functions
. alterator-net-functions
. alterator-hw-functions
. shell-config

mkdir -p $cachedir

###

is_defined()
{
    set|grep -qs "^$(quote_sed_regexp "$1")="
}

is_bridge()
{
    local ifacedir="$1"; shift
    local iface="${ifacedir##*/}"

    if netdev_is_up "$iface"; then
        netdev_is_bridge "$iface"
    else
        [ "$ifacedir" != "$iface" ] || ifacedir="/etc/net/ifaces/$iface"
        [ "$(read_iface_option "$ifacedir" TYPE)" = bri ]
    fi
}

real_iface()
{
    local ifacedir="$1"; shift
    local iface="${ifacedir##*/}"

    [ "$ifacedir" != "$iface" ] || ifacedir="/etc/net/ifaces/$iface"
    if is_bridge "$ifacedir";then
        if netdev_is_up "$iface"; then
            netdev_list_brif "$iface"|head -n1
        else
            read_iface_option "$ifacedir" HOST | sed -rn "s;^['\"]?([[:alnum:]]+).*;\1;p"
        fi
    else
        echo "$iface"
    fi
}

name_with_bridge()
{
    local name="$1"; shift
    local ifacedir=

    if [ -d "$cachedir/br$name" ] && [ "$(real_iface "$cachedir/br$name")" = "$name" ]; then
        echo "br$name"
    elif [ -d "$cachedir/${name#br}" ]; then
        echo "${name#br}"
    else
        echo "$name"
    fi
}

### cache
init_cache()
{
    local name="$1"; shift
    local bridge="${1-}"
    local ifacedir="/etc/net/ifaces/$name"
    local dstdir="$cachedir/$name"
    local srcname=

    if [ ! -d "$dstdir" ] ;then
        if [ -n "$bridge" ]; then
            if test_bool "$bridge"; then
                srcname="${name#br}"
                if [ -d "$cachedir/$srcname" ]; then
                    mv -f -- "$cachedir/$srcname" "$dstdir"
                elif [ -d "$ifacedir" ]; then
                    cp -a "$ifacedir" "$cachedir"
                elif [ -d "/etc/net/ifaces/$srcname" ]; then
                    cp -a "/etc/net/ifaces/$srcname" "$dstdir"
                fi
            else
                srcname="br$name"
                if [ -d "$cachedir/$srcname" ]; then
                    mv -f -- "$cachedir/$srcname" "$dstdir"
                elif [ -d "/etc/net/ifaces/$srcname" ]; then
                    cp -a "/etc/net/ifaces/$srcname" "$dstdir"
                elif [ -d "$ifacedir" ]; then
                    cp -a "$ifacedir" "$cachedir"
                fi
            fi
        else
            [ -d "$ifacedir" ] && cp -a "$ifacedir" "$cachedir"
        fi
        mkdir -p -- "$dstdir"
    fi

    [ ! -f /etc/sysconfig/network -o -f "$cachedir/network" ] || cp /etc/sysconfig/network "$cachedir/network"
}

clear_cache()
{
    rm -rf "$cachedir"
    mkdir -p "$cachedir"
}

commit_hostname()
{
    [ -f "$cachedir/network" ] || return
    ! cmp -s /etc/sysconfig/network "$cachedir/network" || return

    local old_value="$(shell_config_get /etc/sysconfig/network HOSTNAME)"
    local new_value="$(shell_config_get "$cachedir/network"    HOSTNAME)"

    mv -f "$cachedir/network" /etc/sysconfig/network
    hostname "$new_value"
    run-parts /etc/hooks/hostname.d "$old_value" "$new_value"
}

commit_cache()
{
    #little run-parts: check configuration before apply it
    set_locale
    local answer=
    for f in "$precommit_hooks_dir"/*; do
	[ -f "$f" -a -x "$f" ] || continue
	[ "${f%.rpm*}" = "$f" -a "${f%\~}" = "$f" ] || continue

	if ! answer="$("$f")";then
	    [ -n "$answer" ] || answer="$f failed"
	    write_error "$answer"
	    return 1
	fi
    done

    commit_hostname

    find $cachedir -maxdepth 1 -mindepth 1 -type d|
	while read iface; do

	    [ -n "$iface" ] || exit

	    local ifname="${iface##*/}"

        #try to stop and remove old bridge
        if [ -d "/etc/net/ifaces/br$ifname" ] && is_bridge "/etc/net/ifaces/br$ifname"; then
            [ -n "$DURING_INSTALL" ] || iface_down "br$ifname"
            rm -rf -- "/etc/net/ifaces/br$ifname"
        fi

	    [ -n "$DURING_INSTALL" ] || iface_down "$ifname"

	    local old_addresses="$(read_iface_addresses "/etc/net/ifaces/$ifname")"
	    local new_addresses="$(read_iface_addresses "$iface")"

	    local old_configuration="$(read_configuration "/etc/net/ifaces/$ifname")"
	    local new_configuration="$(read_configuration "$iface")"

	    #update configs
	    rm -rf -- "/etc/net/ifaces/$ifname"
	    mv -f -- "$cachedir/$ifname" "/etc/net/ifaces/$ifname"

	    #add config for bridge members
	    if is_bridge "$ifname";then
            local real_fname="$(real_iface "$ifname")"
            if [ -n "$real_fname" ]; then
                [ -n "$DURING_INSTALL" ] || iface_down "$real_fname"
                rm -rf -- "/etc/net/ifaces/$real_fname"
                mkdir -p -- "/etc/net/ifaces/$real_fname"
                printf 'TYPE=eth\nBOOTPROTO=static\n' >"/etc/net/ifaces/$real_fname/options"
            fi
	    fi

	    #try to restart
	    [ -n "$DURING_INSTALL" ] && netdev_is_up "$ifname" || iface_up "$ifname"

	    if [ "$old_addresses" != "$new_addresses" -o "$old_configuration" != "$new_configuration" ];then
		export old_addresses new_addresses old_configuration new_configuration
		run-parts  "$postcommit_hooks_dir"  "$ifname"
	    fi
	done

    clear_cache
    /sbin/update_chrooted conf >&2 || :
}

### hostname
check_hostname()
{
    local hn="$1"
    local length=

    length="$(printf "%s" "$hn" | wc -m)"
    if [ $length -gt $max_hostname_length ]; then
        write_error "`_ "Host name is too long"`"
        return 1
    fi
    return 0
}

read_hostname()
{
	local netconfig="/etc/sysconfig/network"
	[ -f "$cachedir/network" ] && netconfig="$cachedir/network"
	
	local value="$(shell_config_get "$netconfig" HOSTNAME)"

	[ -n "$value" ] || value="localhost.localdomain"
	echo "$value"
}

write_hostname()
{
	local netconfig="$cachedir/network"

	check_hostname "$1" || return
	shell_config_set "$netconfig" HOSTNAME "$1"
	shell_config_del "$netconfig" DOMAINNAME
}

### computer name

read_computer_name()
{
	local value="$(read_hostname)"
	echo "${value%%.*}"
}

read_computer_domain()
{
	local value="$(read_hostname)"
	local domain="${value#*.}"
	[ "$value" != "$domain" ] || domain="localdomain"
	echo "$domain"
}

write_computer_name()
{
    write_hostname "$1.$(read_computer_domain)"
}

### interface work
list_eth_cached()
{
    list_eth | while read name; do
        if [ -d "$cachedir/br$name" ] && [ "$(real_iface "$cachedir/br$name")" = "$name" ]; then
            name="br$name"
        elif [ -d "$cachedir/${name#br}" ]; then
            name="${name#br}"
        elif [ -d "/etc/net/ifaces/br$name" ] && [ "$(real_iface "br$name")" = "$name" ]; then
            name="br$name"
        fi
        echo "$name" 2>/dev/null
    done
}

list_mask()
{
    for i in `seq 32 -1 0`; do 
	write_enum_item "$i" "/$i ($(ipv4addr_prefix_to_mask "$i"))"
    done
}

list_controlled()
{
    local bridge="$1"; shift
    write_enum_item "etcnet" "Etcnet"
    if [ -f "/usr/sbin/NetworkManager" ] && ! test_bool "$bridge"; then
	write_enum_item "NetworkManager" "NetworkManager"
    fi
    write_enum_item "nothing" "`_ "not under control"`"
}

list_configuration()
{
    write_enum_item "dhcp" "`_ "Use DHCP"`"
    write_enum_item "ipv4ll" "`_ "Use Zeroconf"`"
    write_enum_item "static" "`_ "Manually"`"
}

read_info()
{
    local name="$1";shift
    [ -n "$name" ] || return 0

    local info="`_ "Network adaptor:"`"
    info="$info
$(netdev_read_info "$name")"

    if ! netdev_is_wireless "$name"; then
	if netdev_is_plugged "$name";then
	    info="$info
`_ "plugged"`"
	else
	    info="$info
`_ "unplugged"`"
	fi
    fi
    echo "$info"
}

read_controlled()
{
	local nm_controlled="$(read_iface_option "$1" NM_CONTROLLED)"
	local disabled="$(read_iface_option "$1" DISABLED)"

	if [ $(write_bool "$nm_controlled") = "#t" ];then
		echo 'NetworkManager'
	elif [ $(write_bool "$disabled") = "#f" ];then
		echo 'etcnet'
	else
		echo "nothing"
	fi
}

read_configuration()
{
    local bootproto="$(read_iface_option "$1" BOOTPROTO)"

    if [ "$bootproto" = "static" ];then
	echo 'static'
    elif [ "$bootproto" = "ipv4ll" ];then
	echo 'ipv4ll'
    else #dhcp by default
	echo 'dhcp'
    fi
}

get_ifacedir()
{
	local name="$1"; shift

	if [ -d "$cachedir/$name" ];then
	    echo "$cachedir/$name"
	else
	    echo "/etc/net/ifaces/$name"
	fi
}

read_iface_address()
{
	local name="$1"
	local ifacedir="$(get_ifacedir "$name")"
	if ! [ -f "$cachedir/$name-ipv4address" ]; then
	    > "$cachedir/$name-ipv4address"
	    if [ -s "$ifacedir/ipv4address" ]; then
		cat "$ifacedir/ipv4address" > $cachedir/$name-ipv4address
	    fi
	fi
}

read_iface()
{
	local name="$1"; shift
	local ifacedir="$(get_ifacedir "$name")"
	local bridge=no

	#collect general information
	local real_name="$(real_iface "$ifacedir")"
	write_string_param real_name "$real_name"

	write_string_param adaptor "$(read_info "$real_name")"
	write_string_param configuration "$(read_configuration "$ifacedir")"
	write_string_param controlled "$(read_controlled "$ifacedir")"
	write_bool_param wireless "$(netdev_is_wireless "$real_name" && echo "yes" || echo "no")"

	write_string_param dns "$(read_iface_dns "$ifacedir")"
	write_string_param search "$(read_iface_search "$ifacedir")"

	read_iface_address $name

	write_string_param default "$(read_iface_default_gw "$ifacedir")"

	write_string_param ip_string "$(netdev_read_ip "$name"|head -n1)"
	is_bridge "$ifacedir" && bridge=yes
	write_bool_param bridge "$bridge"
}

write_controlled()
{
	local ifacedir="$1";shift
	local controlled="$1";shift

	case "$controlled" in
	    NetworkManager)
		write_iface_option "$ifacedir" DISABLED yes
		write_iface_option "$ifacedir" NM_CONTROLLED yes
		;;
	    etcnet)
		write_iface_option "$ifacedir" DISABLED no
		write_iface_option "$ifacedir" NM_CONTROLLED no
		;;
	    nothing)
		write_iface_option "$ifacedir" DISABLED yes
		write_iface_option "$ifacedir" NM_CONTROLLED no
		;;
	esac
}

write_configuration()
{
	local ifacedir="$1";shift
	local configuration="$1";shift

	 case "$configuration" in
	    static)
		write_iface_option "$ifacedir" BOOTPROTO static
		;;
	    dhcp)
		write_iface_option "$ifacedir" BOOTPROTO dhcp
		;;
	    ipv4ll)
		write_iface_option "$ifacedir" BOOTPROTO ipv4ll
		;;
	esac
}

write_iface()
{
	local name="$1"; shift
	local bridge="${1-}"
	local ifacedir="$cachedir/$name"

	if [ -z "$bridge" ]; then
	    is_bridge "$ifacedir" && bridge="#t" || bridge="#f"
	fi
	if test_bool "$bridge"; then
        # During the creation a new bridge HOST not setted yet
        local host="$(real_iface "$ifacedir")"
        [ "$name" != "$host" ] || host="${name#br}"
	    write_iface_option "$ifacedir" TYPE bri
	    write_iface_option "$ifacedir" HOST "$host"
        [ "$in_controlled" != NetworkManager ] || in_controlled=etcnet
	else
	    write_iface_option "$ifacedir" TYPE eth
	    shell_config_del "$ifacedir/options" HOST
	fi

	write_iface_addresses "$ifacedir" "$(echo $(cat $cachedir/${name}-ipv4address))"

	is_defined "in_default" &&
	    write_iface_default_gw "$ifacedir" "$in_default"

	[ -n "$in_controlled" ] &&
	    write_controlled "$ifacedir" "$in_controlled"

	[ -n "$in_configuration" ] &&
	    write_configuration "$ifacedir" "$in_configuration"

	is_defined "in_dns" &&
	    write_iface_dns "$ifacedir" "$in_dns"

	is_defined "in_search" &&
	    write_iface_search "$ifacedir" "$in_search"
}

add_iface_address()
{
    local iface="$1"
    local addr="$2"
    local mask="$3"
    if [ -n "$iface" -a -n "$addr" -a -n "$mask" ] ; then
	read_iface_address $iface
	echo "$addr/$mask" >> "$cachedir/$iface-ipv4address"
    fi
}

del_iface_address()
{
    local iface="$1"
    local addr="$2"
    if [ -n "$iface" -a -n "$addr" ] ; then
	read_iface_address $iface
	sed -i -e "s|^${addr}$||" -e '/^$/d' "$cachedir/$iface-ipv4address"
    fi
}

list_iface_addresses()
{
    local name="$1"; shift
    read_iface_address $name
    cat "$cachedir/$name-ipv4address" | write_enum
}

#initial actions
iface_up lo
clear_cache

alterator_export_var \
    computer_name	system-computer-name \
    default		ipv4-address \
    search		hostname-list \
    dns			ipv4-address-list
#    addresses 		ipv4-addrwmask-list \

on_message()
{
	case "$in_action" in
		add_iface_address)
		    add_iface_address "$in_real_name" "${in_addip}" "${in_addmask}";;
		del_iface_address)
		    del_iface_address "$in_real_name" "$in_addresses" ;;
		list_iface_address)
		    local name="${in_name}"
		    [ -n "$name" ] || name="$(list_eth_cached|head -n1)"
		    list_iface_addresses "$name"
		    ;;
		list)
			case "${in__objects##*/}" in
			    avail_masks) list_mask ;;
			    avail_configurations) list_configuration ;;
			    avail_controlled) list_controlled "$in_bridge";;
			    *) list_eth_cached|write_enum;;
			esac
			;;
		read)
			local name="${in_name}"
			[ -n "$name" ] || name="$(list_eth_cached|head -n1)"
			name="$(name_with_bridge "$name")"
			case "$in__objects" in
			    /)
				[ -n "$name" ] && read_iface "$name"

				write_string_param name		"$name"

				write_string_param computer_name "$(read_computer_name)"
				write_string_param computer_domain "$(read_computer_domain)"
				;;
			    controlled)
				local controlled="$(read_controlled "$(get_ifacedir "$name")")"
				if test_bool "$in_bridge" && [ "$controlled" = 'NetworkManager' ]; then
				    controlled='etcnet';
				fi
				write_string_param controlled "$controlled"
				;;
			esac
			;;
		write)
			if  [ -n "$in_reset" ]; then
			    clear_cache
			    return
			fi
			local name="${in_name}"
			[ -n "$name" ] || name="$(list_eth_cached|head -n1)"
			init_cache "$name" "$in_bridge"

			[ -n "$name" ] && write_iface "$name" "$in_bridge"

			[ -n "$in_computer_name" ] && write_computer_name "$in_computer_name"
			[ -n "$in_hostname" ] && write_hostname "$in_hostname"

			if [ -n "$in_commit" ]; then
			    commit_cache || return
			fi
			;;
	esac
}

message_loop
