#!/bin/bash 

# Copyright (c) 2017-2018 by Cisco Systems, Inc.

# Provides config related functions for netns files

source /usr/lib/app-hosting-log-functions

APP_HOST_DEFAULT_VRF="global-vrf"

#
# Handle error logging for a vrf 
#
function vrf_netns_update_status()
{
    local vrf="$1"
    local message="$2"
    
    #
    # Check if the vrf is a tpa vrf, log to syslog and return error if yes,
    # log to file and return success
    #
    is_vrf_tpa "$vrf"
    if [[ $? -eq 0 ]] ; then
        app_hosting_log_syslog_error "$message"
        false
        return
    else
        app_hosting_log "$message"
        true
        return
    fi
}

# Update domain or nameserver entries in $resolv_file
# e.g.:
#       update_resolv_conf ADD nameserver 1.1.1.1
#       update_resolv_conf DELETE nameserver dns.cisco.com
#       update_resolv_conf SET domain cisco.com
#       update_resolv_conf CLEAR domain
function update_resolv_conf()
{
    local action="$1"
    local category="$2"
    local value="$3"
    local vrf="$4"
   
    if [[ -z "$vrf" ]] || [[ "$vrf" == "default" ]]; then
        vrf="$APP_HOST_DEFAULT_VRF"
    fi 

    local resolv_file="/etc/netns/$vrf/resolv.conf"

    if [[ ! -d /etc/netns/$vrf ]]; then
        vrf_netns_update_status "$vrf" "Directory /etc/netns/$vrf is missing."
        return $?
    fi

    # Do we actually need to do any work?
    category_value_found=1
    category_found=1
    if [[ -f $resolv_file ]]; then
        grep -q "^$category *$value *$" $resolv_file
        category_value_found=$?
        grep -q "^$category " $resolv_file
        category_found=$?
    fi
    if [[ "$action" == "ADD" ]] || [[ "$action" == "SET" ]]; then
        if [[ "$category_value_found" -eq 0 ]]; then
            app_hosting_log "Entry for $category $value already in $resolv_file"
            return 0
        fi
    elif [[ "$action" == "DELETE" ]]; then
        if [[ "$category_value_found" -ne 0 ]]; then
            app_hosting_log "No entry for $category $value found in $resolv_file"
            return 0
        fi
    elif [[ "$action" == "CLEAR" ]]; then
        if [[ "$category_found" -ne 0 ]]; then
            app_hosting_log "No entry for $category found in $resolv_file"
            return 0
        fi
    fi

    app_hosting_log "Attempting to $action $category $value in $resolv_file"
    local tmp_resolv_file=`mktemp -t resolv_tmp.XXXXXXXXXX`
    if [[ -f $resolv_file ]]; then
        $APP_HOSTING_LOG_EXEC cp $resolv_file $tmp_resolv_file
    else
        $APP_HOSTING_LOG_EXEC touch $tmp_resolv_file
    fi

    # APP_HOSTING_LOG_EXEC is not very good about preserving quoting,
    # so we don't use it here.
    if [[ "$action" == "ADD" ]]; then
        echo "$category $value" >> $tmp_resolv_file
    elif [[ "$action" == "DELETE" ]]; then
        sed -i "/^$category *$value *$/d" $tmp_resolv_file
    elif [[ "$action" == "SET" ]]; then
        # Basically a CLEAR then ADD
        if [[ "$category_found" -eq 0 ]]; then
            sed -i "/^$category .*$/d" $tmp_resolv_file
        fi
        echo "$category $value" >> $tmp_resolv_file
    elif [[ "$action" == "CLEAR" ]]; then
        sed -i "/^$category .*$/d" $tmp_resolv_file
    fi

    rc=$?
    if [[ $rc -ne 0 ]]; then
        vrf_netns_update_status "$vrf" "Failed to $action $category $value in resolv.conf"
        rc=$?
        $APP_HOSTING_LOG_EXEC rm $tmp_resolv_file
        return $rc
    fi

    # For whatever reason, "rm $resolv_file" and
    # "mv -f $tmp_resolv_file $resolv_file" intermittently fail with a
    # "Device or resource busy" error. Also for whatever reason,
    # using "cat" to overwrite the file in place does not have this problem.
    app_hosting_log "Moving $tmp_resolv_file to $resolv_file"
    cat $tmp_resolv_file > $resolv_file
    rc=$?
    if [[ $rc -ne 0 ]]; then
        vrf_netns_update_status "$vrf" "Failed to update $resolv_file for vrf $vrf"
        rc=$?
        $APP_HOSTING_LOG_EXEC rm $tmp_resolv_file
        return $rc
    fi

    $APP_HOSTING_LOG_EXEC rm $tmp_resolv_file
    app_hosting_log "Successfully updated $resolv_file."
    
    return 0
}

#
# Get the domain name corresponding to a vrf
#
function get_vrf_domain_name()
{
    local vrf=$1
    local vrf_domain_name=""
  
    if [[ -f "/etc/netns/$vrf/resolv.conf" ]]; then
        vrf_domain_name=$(grep "^ *\<domain\> *" /etc/netns/$vrf/resolv.conf 2>/dev/null | awk "{print \$2}")
    fi 
            
    if [[ -z "$vrf_domain_name" ]]; then
        if [[ "$hostname" == "localhost" ]]; then
            vrf_domain_name="localdomain"
        else
            vrf_domain_name="(none)"
        fi
    fi

    echo "$vrf_domain_name"
}

#
# Check if the vrf is a tpa enabled vrf
# Check for the existence of fwdintf
#
function is_vrf_tpa()
{
    local vrf=$1
    
    ip netns exec "$vrf" ifconfig 2>/dev/null | grep -q "fwdintf"
    return $?
}
      
#
# Get the domain name for the given vrf and invoke 
# the function to udate the /etc/netns/vrf/hosts file 
# 
#
function update_etc_hosts()
{
    local hostname=$1
    local vrf=$2
    local rc=0
    local final_ret=0

    if [[ "$vrf" == "default" ]]; then
        vrf="$APP_HOST_DEFAULT_VRF"
    fi 

    #
    # If a specific vrf was passed to this function,only  
    # update the /etc/netns/$vrf/hosts file corresponding to 
    # the vrf
    #
    if [[ -n "$vrf" ]]; then
        vrf_domain_name=$(get_vrf_domain_name "$vrf" 2>/dev/null)
        update_etc_hosts_vrf "$hostname" "$vrf_domain_name" "$vrf"
        return $?
    fi 
      
    # 
    # If no vrf was passed to this function , 
    # update the hostname in all the vrfs except xrnns
    # and tpnns 
    #    
    local vrf_list=$(ip netns list 2>/dev/null)
    for vrf in $vrf_list
    do
        case "$vrf" in
        xrnns | tpnns)
            continue
            ;;
        *)
            vrf_domain_name=$(get_vrf_domain_name "$vrf" 2>/dev/null)
            update_etc_hosts_vrf "$hostname" "$vrf_domain_name" "$vrf"
            rc=$?            
            if [[ $rc -ne 0 ]]; then 
                final_ret=$rc
            fi 
            ;;
        esac
    done

    return $final_ret
}

# Update the hostname and/or domain-name in $hosts_file
# e.g.:
#       update_etc_hosts myhostname mydomain.com <vrf>
#       update_etc_hosts '' mydomain.com <vrf>
#       update_etc_hosts myhostname '(none)' <vrf>
function update_etc_hosts_vrf()
{
    local hostname="$1"
    local domain_name="$2"
    local vrf="$3"
    local rc=0

    if [[ -z "$vrf" ]] || [[ "$vrf" == "default" ]]; then 
        vrf="$APP_HOST_DEFAULT_VRF"
    fi 

    if [[ ! -d /etc/netns/$vrf ]]; then
        vrf_netns_update_status "$vrf" "Directory /etc/netns/$vrf is missing. Failed to update the hosts file."
        return $?
    fi

    if [[ -z "$hostname" ]]; then
        hostname="localhost"
    fi

    if [[ -z "$domain_name" ]] || [[ "$domain_name" == '(none)' ]]; then
        if [[ "$hostname" == "localhost" ]]; then
            domain_name="localdomain"
        else
            domain_name=""
        fi
    fi

    local hosts_file="/etc/netns/$vrf/hosts"
    local my_local_ip="127.0.1.1"
    local tmp_hosts_file=`mktemp -t hosts_tmp.XXXXXXXXXX`
    if [[ -f $hosts_file ]]; then
        $APP_HOSTING_LOG_EXEC cp $hosts_file $tmp_hosts_file

        app_hosting_log "Removing existing entry from $tmp_hosts_file"
        # APP_HOSTING_LOG_EXEC is not very good about preserving quoting,
        # so we don't use it here.
        sed -i "/^$my_local_ip /d" $tmp_hosts_file
        rc=$?
        if [[ $rc -ne 0 ]]; then
            vrf_netns_update_status "$vrf" "Failed to update /etc/netns/$vrf/hosts."
            rc=$?
            $APP_HOSTING_LOG_EXEC rm $tmp_hosts_file
            return $rc
        fi
    else
        $APP_HOSTING_LOG_EXEC touch $tmp_hosts_file
    fi

    app_hosting_log "Setting new entry in $tmp_hosts_file"
    if [[ -z "$domain_name" ]]; then
        # Entry with hostname but no domain-name
        echo "$my_local_ip    $hostname" >> $tmp_hosts_file
    else
        # Entry with hostname and domain-name
        echo "$my_local_ip    $hostname.$domain_name    $hostname" >> $tmp_hosts_file
    fi

    rc=$?
    if [[ $rc -ne 0 ]]; then
        vrf_netns_update_status "$vrf" "Failed to update /etc/netns/$vrf/hosts."
        rc=$?
        $APP_HOSTING_LOG_EXEC rm $tmp_hosts_file
        return $rc
    fi

    # For whatever reason, "rm $hosts_file" and
    # "mv -f $tmp_hosts_file $hosts_file" intermittently fail with a
    # "Device or resource busy" error. Also for whatever reason,
    # using "cat" to overwrite the file in place does not have this problem.
    app_hosting_log "Moving $tmp_hosts_file to $hosts_file"
    cat $tmp_hosts_file > $hosts_file
    rc=$?
    if [[ $rc -ne 0 ]]; then
        vrf_netns_update_status "$vrf" "Failed tp update $hosts_file."
        rc=$?
        $APP_HOSTING_LOG_EXEC rm $tmp_hosts_file
        return $rc
    fi

    $APP_HOSTING_LOG_EXEC rm $tmp_hosts_file

    app_hosting_log "Successfully updated $hosts_file."
}
