#!/bin/sh

############
# CHANGELOG
#===========
#
# 2012/01/18    PR341502        Ziming Xu
#               Reduce Reboot Frequency
#
# 2013/01/15    PR411913        Ting He
#				Change log level of function echolog from emerg to notice
#
# 2013/01/24    PR261951        Jerry ZHOU
#               change alias definition for "ENTER_FUNCTION" and "RETURE"
#
# 2012/09/10    crms00395199    Ziming Xu
#		Remove "ENETCFG_DM_PORT" "ENETCFG_DM_BACKUP_PORT" from preserve list
#
# 2011/12/15    crms00351686    Michel SULYAN
#               add new parseUrl() function
#
# 2011/11/09    CRMS00331041    Jerry ZHOU
#               Current 'status' settings may be lost if they are updated during DM config files treatment
#
# 2011/08/11    CRMS00331013    Michel SULYAN
#               Add settings to 'resetVars' list to preserve CURRENT values
#
# 2011/05/30    CRMS00312748    Thanh lam NGUYEN
#               Fix multiple instance of either the daemon or the init script
#
# 2011/03/04    CRMS00296221    Abdelfattah CHEHAB
#		Add function to verify integrity of setting file and cleanup if necessary
# 
# 2011/01/14    CRMS00286717    Thanh lam NGUYEN
#               Add echolog to display on stderr and log the message in Defence log.
#
# 2010/10/29    CRMS00267921    Thanh lam NGUYEN
#               Fix updateCurrentConfig to manage multiple vlan in ENETCFG_INTERFACE 
#
# 2010/08/18    CRMS00258266    Thanh lam NGUYEN
#               Fix functions return code
#               as "RETURN" is an alias which ends in "return", the return code has to be an immediate or memorized in a variable !!!
#
# 2010/08/18    CRMS00253725    Thanh lam NGUYEN
#               Fix the infinite loop
#
# 2010/08/17    CRMS00253434    Thanh lam NGUYEN
#               Fix the scope of the "setting" variable
#
# 2010/08/17    CRMS00245891    Thanh lam NGUYEN
#               Fix the register value to force the MDIX to be compabtible with the CSP#277799
#
# 2010/07/30    CRMS00246998    Thanh lam NGUYEN
#               Save also the protocol
#
# 2010/06/21    CRMS00234788_CSP323209_force_MDIX    Thanh lam NGUYEN
#               Force MDIX on PC port
#
# 2010/05/24    CRMS00232512    Alex CAO
#               Fix static setting with no ENETCFG_VLAN_ENABLE
#
# 2010/05/06    DEMO_MODE       Thanh lam NGUYEN
#               Modification to take into account the demo mode
#
# 2010/03/26    _ADD_POPALL     Thanh lam NGUYEN
#               pop all elements from a push variables
#
# 2010/03/26    CRMS00225368    Thanh lam NGUYEN
#               Fix the content of ENETCFG_INTERFACE for NVCURRENT_ROOT
#
# 2010/03/26    CRMS00219460    Thanh lam NGUYEN
#               Fix the downloading order for dhcp
#
# 2010/02/26    CRMS00152511    Thanh lam NGUYEN
#               Reorganize the content
#               Extend readConfig & readCurrentConfig to support the files from configFileList
#               Add ENTER_FUNCTION & RETURN alias
#               Add updateCurrentConfig
#
# 2010/08/25    CSP314947      Fiona LI
#               Modify module loading order to adapt Hotfix-2 of CSP#314947 (solve no network issue)
#
# 2011/04/28    crms00302752   Jason Zhang
#               No directory tree show in ftp mode
#
# 2011/07/06    crms00325387   Jason Zhang
#               8082 acting has firewall
#
############

# NOTE:
#   RETURN macro in a short 'if' has to be like
#           [[ -f <file> ]] && { RETURN <ret_code>; }
#   The '{' and '; }' are VERY VERY important


[[ "${NV_ROOT:-none}" == "none" ]] && source /etc/sysconfig

#CRMS00296221

# cleanCurreptedFile
#---------------
# ARGS:
#   <none>  Clean corrupted file
cleanCurreptedFile()
{
    tempCleanFile="$1.clean.tmp"
    rm $tempCleanFile > /dev/null 2>&1
    touch $tempCleanFile 
    while read line
    do
      echo $line |grep '^[A-Z_]*=\"*\"' >/dev/null
      intret=$?
      if [ $intret -eq 0 ]
      then
          echo $line >> $tempCleanFile
      fi
    done < $1
    mv $tempCleanFile $1
}

# checkConfigFiles
#---------------
# ARGS:
#   <none>  Check file for source command and cleanup if necessary
checkConfigFiles()
{
for dir in $NVLOCAL_ROOT $NVDHCP_ROOT $NVLLDP_ROOT $NVDM_ROOT; do
  for cfg in $configFileList; do
    if [[ -f $dir/$cfg ]];
    then
        /bin/sh -c "source $dir/$cfg" >/dev/null 2>&1
        ret=$?
        if [ $ret -ne 0 ]
        then
            if [ "$dir" == "$NVLOCAL_ROOT" ]
            then
		echoerror "Corrupted config file $dir/$cfg, removing bad line"
                cp -f "$dir/$cfg" "$dir/$cfg.corrupted"
                cleanCurreptedFile "$dir/$cfg"

            else
		echoerror "Corrupted config file $dir/$cfg, removing whole directory"
                rm -rf "$dir"
                mkdir "$dir"
            fi
        fi
    fi
  done
done
}

#CRMS00296221 end


# define_aliases
#---------------
# ARGS:
#   <none>  define macros that are used in the different functions
define_aliases() {
    local -
    set +x

# CRMS00312748
    esc=$'\x1b'
    attr="$esc[7m"
    errorattr="$esc[31;7m"
    default="$esc[0m"
    ROOT=${ROOT:-}
# CRMS00312748 END
    alias DEFAULT_DEBUG_OFF="local ldebug=1"
# CRMS00312748
    alias defPS4="PS4=\${PS4_TPLT:-\"\$esc[\$((\$\$%6+31))m+(\$\$)\$FUNCTION:\$esc[0m \"}"
    alias IN_DEBUG="([[ -f \$ROOT/home/admin/debug.\$FUNCTION ]] || [[ -f \$ROOT/.debug.\$FUNCTION ]] || [[ -f \$ROOT/tmp/debug.\$FUNCTION ]])"
    alias IN_NODEBUG="([[ -f \$ROOT/home/admin/nodebug.\$FUNCTION ]] || [[ -f \$ROOT/.nodebug.\$FUNCTION ]] || [[ -f \$ROOT/tmp/nodebug.\$FUNCTION ]])"
 
#PR261951 jerryzh+   
    if [[ -f $ROOT/home/admin/.dmconfig.debug ]] || [[ -f $ROOT/tmp/.dmconfig.debug ]]; then
        alias ENTER_FUNCTION="
        [[ \${ldebug:-0} -eq 1 ]] && { local -; set +x; }
        : \"---- ENTER_FUNCTION\"
        : \"\$esc[4mfrom \${EF_function:-unknown()}\${default}\"
        local OIFS=\$IFS IFS=\",\"
        local EF_function=\"\$FUNCTION(\"\"\$*\"\")\"
        IFS=\$OIFS
        local _in_xflag=\${-//[^x]}
        local PS4
        defPS4
        if IN_DEBUG; then
            local -; set -x
        fi
        if IN_NODEBUG; then
            local -; set +x
        fi
        (IFS=\",\"; : \"\${attr}START \$EF_function\${default}\")
        : \"---- ENTER_FUNCTION END\"
        "
# CRMS00312748 END

        #   NOTE:
        #       - RETURN macro in a short 'if' has to be like
        #           [[ -f <file> ]] && { RETURN <ret_code>; }
        #         The '{' and '; }' are VERY VERY important
        #       - The ret_code has to be an immediate or a variable other than "$?".
        #         "$?" is destroyed by the RETURN macro.
        alias RETURN="
        : \"\${attr}LEAVING \$EF_function\${default}\"
        if [[ \"\$_in_xflag\" == \"\${-//[^x]}\" ]]; then
            [[ -n \"\$_in_xflag\" ]] && set -x || set +x
        fi
        return"
    else
        alias ENTER_FUNCTION=""
        alias RETURN="return"
    fi
#PR261951 jerryzh-

    alias defineSwitchConst="
    local  RES=0x00FF   # reserved
    local   FD=0x0100   # Full Duplex
    local   RA=0x0200   # Restart Auto-negotiation
    local IPFM=0x0400   # Isolate Port From Mii
    local   PD=0x0800   # Power Down
    local    A=0x1000   # Auto-negotiation
    local F100=0x2000   # Force 100 Mb
    local    L=0x4000   # Loopback
    local   SR=0x8000   # Soft Reset

    local ETHLCFG_SMP=8
    local ETHSW_PC=0x001
    local ETHSW_LAN=0x002
    local ETHSW_SMP=0x100
    "
    alias defineForceSettings="
    local forceSettings=\"ENETCFG_IPADDR ENETCFG_ROUTER ENETCFG_DNS1 ENETCFG_DNS2 ENETCFG_DNS3 WPA8021XCFG_MD5_PASSWD\"
    "
 
# CRMS00331041   
    alias defineStatusSettings="
    local statusSettings=\"ENETCFG_SNTP_SYNC_STATUS ADMCFG_INVENTORY_STATUS\"
    "
# CRMS00331041 END
   
    alias defineConfigFileList="
    local configFileList=\"admin.cfg enet.cfg ethlink.cfg lldp.cfg security.cfg wpa8021x.cfg\"
    "
# CRMS00253725
    alias gotoInfiniteLoop="mkfifo /tmp/__reboot 2>/dev/null; read 2>/dev/null </tmp/__reboot >/dev/null infiniteloop"
# CRMS00253725 END

    eval "alias set_all_settings=\"local all_settings=\\\"$(x=$(CLISettings listname | sort); echo $x)\\\"\""
    mutexdir=/var/run/.mutex
}
define_aliases

# CRMS00246998
# resetVars
#----------
# ARGS:
#   <none>  Reset the settings to their CURRENT value if defined
resetVars() {
# CRMS00253434
    local FUNCTION=resetVars cmd tmp setting
    DEFAULT_DEBUG_OFF
    ENTER_FUNCTION
    
# CRMS00253434 END
    cmd="ENETCFG_MACADDR=$(CLISettings get CURRENT ENETCFG_MACADDR)"
# PR341502 zimingxu+
# Remove "ENETCFG_DM_URL" "ENETCFG_DM_PROTOCOL" "ENETCFG_DM" "ENETCFG_DM_CONFIG_PATH" "ENETCFG_DM_BACKUP_URL" 
# "ENETCFG_DM_BACKUP_PROTOCOL" "ENETCFG_DM_BACKUP" "ENETCFG_DM_BACKUP_CONFIG_PATH" from preserving list
# CRMS00331013
# CRMS00395199 zimingxu-
# Remove "ENETCFG_DM_PORT" "ENETCFG_DM_BACKUP_PORT" from preserving list
    for setting in ENETCFG_DM_SURVIVABILITY_MODE\
                   ENETCFG_DHCP_SURVIVABILITY_MODE\
                   ENETCFG_SNTP_SYNC_STATUS\
                   ADMCFG_INVENTORY_STATUS\
                   WPA8021XCFG_AUTHENTICATED
# CRMS00395199 zimingxu+
# CRMS00331013 END
# PR341502 zimingxu-
    do
        cmd="${cmd:+$cmd}
        tmp=\$(CLISettings get CURRENT $setting --nodefault 2>/dev/null)
        \[[ -n \"\$tmp\" ]] && [[ \"\$$setting\" != \"\$tmp\" ]] && $setting=\$tmp"
    done
    eval "$cmd"
    RETURN 0
}
# CRMS00246998 END


# updateCurrentConfigFiles
#-------------------------
# ARGS:
#   <none>              Update all the files of /config/current
#   file1 [file2] ...   Update the given files of /config/current
updateCurrentConfigFiles() {
    local FUNCTION=updateCurrentConfigFiles attr default
    ENTER_FUNCTION
    local file="$@" cmd

    set_all_settings
# CRMS00331041    
    cmd="$(export $all_settings; awk -v force="$forceSettings" -v status="$statusSettings" '
# CRMS00331041 END
    BEGIN {
        split(force,a," ")
        # get default settings for variables which needs "--force"
        CLI="CLISettings"
        for (var in a) {
            cmd=CLI" get DEFAULT "a[var]
            cmd|getline b[a[var]]
            close(cmd)
        }
# CRMS00331041        
        # parse status setttings that will be ignored.
        split(status,ignored," ")
# CRMS00331041 END     
    }
    /^[[:blank:]]*(#.*)*$/{
        next
    } 
    {
        split($0,item,"=")
        $0=item[1]
        force=""
    }
# CRMS00331041    
    # status setttings that will be ignored. Only the process that is in charge of the status would update them
    {
        for (var in ignored) {
            if ( $0 == ignored[var] ) {
    	        next
    	    }
        }
    }
# CRMS00331041 END   
# CRMS00267921
    /^ENETCFG_INTERFACE$/ {
        if ( ENVIRON["ENETCFG_VLAN_ENABLE"]=="true" ) {
            interface=ENVIRON["ENETCFG_INTERFACE"]
            sub( /\..*$/, "", interface )
            ENVIRON["ENETCFG_INTERFACE"]=interface"."ENVIRON["ENETCFG_VLAN"]
        }
    }
# CRMS00267921 END
    /^ENETCFG_VLAN$/ {
        if ( ENVIRON["ENETCFG_VLAN_ENABLE"]=="false" ) {
            ENVIRON["ENETCFG_VLAN"]="no"
            force="--force"
        }
    }
    {
        if ( $0 in b && b[$0]==ENVIRON[$0]) {
            force="--force"
        }
        print CLI" set CURRENT "$0" \""ENVIRON[$0]"\" "force
    }' $file | sort )"
    eval "$cmd"
    RETURN 0
}

# updateCurrentConfig
#--------------------
# ARGS:
#   <none>      Read /config and update /config/current with the read /config
#   --noread    Skip the read of /config and update /config/current with current environment variables
updateCurrentConfig() {
    local FUNCTION=updateCurrentConfig attr default
    ENTER_FUNCTION
# CRMS00253434
    local cfgfiles="$@" \
          noread=0 \
          source \
          file tmp
# CRMS00253434 END

    defineForceSettings
# CRMS00331041    
    defineStatusSettings
# CRMS00331041 END    
    defineConfigFileList

    : "${attr}Checking for options${default}"
    while [[ "${cfgfiles//--}" != "$cfgfiles" ]]; do
        # still option in cfgfiles
        # find the option
        tmp=${cfgfiles#*--} tmp=${tmp%[[:blank:]]*}
        : "#${attr}option: $tmp${default}"
        case "$tmp" in
            noread)
                noread=1
            ;;
# CRMS00331041            
            source=*)
            	source=${tmp##source=}
            ;;
# CRMS00331041 END           
        esac
        # remove the option
        cfgfiles=${cfgfiles//--$tmp}
    done
    [[ $noread -eq 0 ]] && readConfig
    
    tmp=
    if [[ -n "${cfgfiles//[[:blank:]]}" ]]; then
        for file in $cfgfiles; do
            file=${file##*/}
            # if the config file is not in the list, skip it
            [[ "${configFileList//$file}" == "$configFileList" ]] && : "${attr} $file not in our config file list${default}" && continue
# CRMS00331041            
            if [[ -e $source/$file ]]; then    
            	: "${attr} adding $source/$file to update list${default}"
            	tmp="${tmp:+$tmp }$source/$file"
            else
# CRMS00331041 END            
            	: "${attr} adding $NVDEFAULT_ROOT/$file to update list${default}"
            	tmp="${tmp:+$tmp }$NVDEFAULT_ROOT/$file"
# CRMS00331041       
            fi
# CRMS00331041 END            
        done
    else
        tmp="$NVDEFAULT_ROOT/*.cfg"
    fi

    [[ -n "${tmp//[[:blank:]]}" ]] && updateCurrentConfigFiles "$tmp"
    RETURN 0
}

# readConfig
#-----------
# ARGS:
#   <none>  Read /config using the following priority:
#           DEFAULT -> LOCAL [-> DHCP] -> LLDP -> DM
# Note:
# - DHCP is skipped if in static mode
# - A filter is applied to remove settings we don't want to use.
#   For example: in /config/local/enet.cfg all ENETCFG_DM are ignored except for ENETCFG_DM_URL & ENETCFG_DM_BACKUP_URL
# - At the end use "resetVars" to reset settings that have not to be modified
readConfig() {
    local FUNCTION=readConfig attr default
    DEFAULT_DEBUG_OFF
    ENTER_FUNCTION
    local IFS dir cfg text

    defineConfigFileList

    # read config values
# CRMS00219460
    : "${attr}for dir in $NVDEFAULT_ROOT $NVLOCAL_ROOT $NVDHCP_ROOT $NVLLDP_ROOT $NVDM_ROOT${default}"
    for dir in $NVDEFAULT_ROOT $NVLOCAL_ROOT $NVDHCP_ROOT $NVLLDP_ROOT $NVDM_ROOT; do
        # skip DHCP config if we are in Static
        if [[ "$dir" == "$NVDHCP_ROOT" ]] && [[ "$ENETCFG_DHCP_MODE" == "Static" ]]; then
            : "${attr}Static mode => skipping $dir/$cfg${default}"
            continue
        fi
# CRMS00219460 END
        : "${attr}dir=$dir${default}"
        for cfg in $configFileList; do
            : "${attr}doing $dir/$cfg${default}"
            ##########
            # FILTER #
            ##########
            if [[ -f $dir/$cfg ]]; then
                case "$dir/$cfg" in
                    $NVLOCAL_ROOT/enet.cfg) : "${attr}FITLERING${default}"
                        eval "$(awk '/ENETCFG_DM(_BACKUP)*_URL/{print}/ENETCFG_DM.*/{next}{print}' $dir/$cfg)";
                    ;;
                    *) : "${attr}NO FITLERING${default}"
                        source $dir/$cfg
                    ;;
                esac
            fi
        done
    done
    # reset vars
    resetVars
    RETURN 0
}

# readCurrentConfig
#------------------
# ARGS:
#   <none>  Read /config/current
readCurrentConfig() {
    local FUNCTION=readCurrentConfig attr default
    DEFAULT_DEBUG_OFF
    ENTER_FUNCTION
# CRMS00253434
    local ret=0 cfg
# CRMS00253434 END

    defineConfigFileList

    readConfig
    for cfg in $configFileList; do
        if [[ -f $NVCURRENT_ROOT/$cfg ]]; then
            : "${attr} reading $NVCURRENT_ROOT/$cfg${default}"
            . $NVCURRENT_ROOT/$cfg
        else 
            ret=1
        fi
    done
    # reset unwanted vars
    resetVars
    RETURN $ret
}

# getCode
#--------
# ARGS:
#   enable auto speed duplex    Calculate the "eth mii" code to use to have the wished confgiuration
#
# Note:
# - The result is in the "getCode_result" environment variable
getCode() {
    local FUNCTION=getCode attr default
    ENTER_FUNCTION
    local enable=$1 auto=$2 speed=$3 duplex=$4
    # getCode_result is the result of getCode, so cannot be local !!!

    : "${attr}check arguments${default}"
    case "$enable" in
	[Ff][Aa][Ll][Ss][Ee]*|[Nn][Oo]*|[Dd][Ii][Ss][Aa][Bb][Ll][Ee]*) : "${attr}Disable interface${default}"
            enable=no
        ;;
        *) : "${attr}Enable interface${default}"
            enable=yes
        ;;
    esac
    case "$auto" in
	[Ff][Aa][Ll][Ss][Ee]*|[Nn][Oo]*|[Dd][Ii][Ss][Aa][Bb][Ll][Ee]*) : "${attr}Not in auto negotiation mode${default}"
            auto=no
        ;;
        *) : "${attr}Auto negotiation${default}"
            auto=yes
        ;;
    esac
    case "$speed" in
        10|100) : "${attr}recognized speed${default}"
        ;;
        *) : "${attr}unrecognized speed${default}"
            speed=100
        ;;
    esac
    case "$duplex" in
        [Hh][Aa][Ll][Ff]) : "${attr}Half duplex${default}"
            duplex=half
        ;;
        *) : "${attr}Full duplex${default}"
            duplex=full
        ;;
    esac

    : "${attr}compute getCode_result${default}"
    defineSwitchConst

    getCode_result=0
    if   [[ "$enable" == "no" ]]; then
        getCode_result=$((PD))
    elif [[ "$auto" == "yes" ]]; then
        getCode_result=$((A|RA))
    else
        [[ "$duplex" == "full" ]] && getCode_result=$FD
        [[ $speed -eq 100 ]] && getCode_result=$((getCode_result|$F100))
    fi
    # The value is in the environment variable 'getCode_result'
    : "${attr}getCode_result: 0x$(printf "%x" $getCode_result)${default}"
    RETURN 0
}

# configEthHw
#------------
# ARGS:
#   <none>  Configure the physical characteristic of the PC and LAN port.
configEthHw() {
    local FUNCTION=configEthHw  attr default
    ENTER_FUNCTION
    defineSwitchConst
# CRMS00253434
    local ret=0 changed=0 mask=$((~(SR|L|IPFM|RA|RESERVED))) \
          dummyVlan ENABLE AUTO SPEED DUPLEX ETHL_INT port getCode_result usleeppid miiget
# CRMS00253434 END

    for port in PC LAN; do
        : "${attr}set the physical configuration for $port port${default}"
        eval "
            AUTO=\${ETHLCFG_${port}_AUTO:-}
            SPEED=\${ETHLCFG_${port}_SPEED:-}
            DUPLEX=\${ETHLCFG_${port}_DUPLEX:-}
            ETHL_INT=\${ETHLCFG_${port}:-}
            [[ \"\${SECUCFG_${port}_PORT:-}\" == \"true\" ]] && ENABLE=false || ENABLE=true
        "
        getCode $ENABLE $AUTO $SPEED $DUPLEX
        miiget=$(eth mii $ETHL_INT 0 '?')
        if [[ "$((miiget & mask))" != "$((getCode_result & mask))" ]]; then
            changed=1
            eth mii $ETHL_INT 0 $getCode_result || ret=$?
        fi
# CRMS00234788_CSP323209
        : "${attr}force MDIX auto-negotiation${default}"
# CRMS00245891
        eval "eth mii \$ETHLCFG_${port} 0x18 0x8217"
# CRMS00245891 END
# CRMS00234788_CSP323209 END
    done
# Crms00463255 tingh
    eth mii 0 0x1c 0xa801  # force power-saving mode to off for PC port
# Crms00463255 tingh END
    case "$SECUCFG_PC_PORT_VLAN_FILTER" in
        [Ff][Ii][Ll][Tt][Ee][Rr]*) : "${attr}filter${default}"
# [[	            dummyVlan=0xFFF	]]
# [[	            eth vlanInit	]]
# [[	            # create a dummy vlan entry	]]
# [[	            eth vlanCreate $dummyVlan	]]
# [[		]]
# [[	            # add vlan tag to all untagged frames from PC, LAN and SMP port	]]
# [[	            eth vidSet $ETHLCFG_PC $dummyVlan	]]
# [[	            eth vidSet $ETHLCFG_LAN $dummyVlan	]]
# [[	            eth vidSet $ETHLCFG_SMP $dummyVlan	]]
# [[	            	]]
# [[	            # allow the switching of vlan $dummyVlan to all port and strip the value to PC and LAN port 	]]
# [[	            eth vlanAdd $dummyVlan $((ETHSW_PC+ETHSW_LAN+ETHSW_SMP)) $((ETHSW_PC+ETHSW_LAN))	]]
# [[		]]
# [[	            # enable vlan filtering	]]
# [[	            eth vlanEnSet 1	]]
        ;;
    esac

    : "${attr}activate the DOS protection by direct writing to registers${default}"
#crms00302752 jasonaz+
#crms00325387 jasonaz+
    /usr/bin/dbg wl 0x3041b000 0x00000000 >/dev/null 2>&1
#crms00325387 jasonaz-
#crms00302752 jasonaz-
    : "${attr}program eth registers to allow 802.1x packet forwarding${default}"
    /usr/lib/sysinit/cfp_cmds.sh

    : "${attr}update current config if needed${default}"
    if [[ $changed -ne 0 || ! -e $NVCURRENT_ROOT/ethlink.cfg || ! -e $NVCURRENT_ROOT/security.cfg ]]; then
        if [[ $changed -ne 0 ]]; then
            # start a sleep of 50000us to allow the parameters to be integrated
            # during that time
            usleep 50000 &
            usleeppid=$!
        fi
        updateCurrentConfig ethlink.cfg security.cfg --noread
        [[ $changed -ne 0 ]] && wait $usleeppid
    fi
    RETURN $ret
}

# loadNetworkModules
#-------------------
# ARGS:
#   <none>  Add the network modules
loadNetworkModules() {
    local FUNCTION=loadNetworkModules attr default
    ENTER_FUNCTION
    local ret=0

    : "${att}Add ethernet modules${default}"
	
#CSP314947, Fiona Li+
    # add ethernet driver
    modprobe $ENETCFG_INTERFACE || ret=$?
    # add ethernet switch driver
    modprobe bcmring_eth_sla || ret=$?
#CSP314947, Fiona Li-

    RETURN $ret
}


# display
#--------
# ARGS:
#   message     Display on the serial line a boxed message
display() {
    local FUNCTION=display attr default
    ENTER_FUNCTION
    local MSG=$1 ret=0 BOX line max

    # find the max width of the different line
    max=0
    max=$(printf "$MSG\n" | while read line; do [ ${#line} -gt $max ] && { max=${#line}; printf " $max"; };done)
    max=${max##* }
    # create the boundary lines
    BOX=$( printf "%0$((max+10))d" 0 | tr "0" "=")
    : "${attr}BOX= [$BOX]${default}"

    # display the message
    printf "\n"
    printf "$attr|$BOX|$default\n"
    printf "$MSG\n" | while read line; do printf "${attr:-}|==== %-*s ====|${default}\n" $max "$line"; done
    printf "$attr|$BOX|$default\n"
    RETURN $ret
}

# checkLinkStatus
#----------------
# ARGS:
#   <none>  Wait for the LAN physical link to be connected
#
# NOTE:
# - a check is done every 500us until the link is connected
checkLinkStatus() {
    local - FUNCTION=checkLinkStatus attr default
    ENTER_FUNCTION
    local ret=0 waittime=0 usleeptime=500 i=0 skipflag=".skip.checkLinkStatus"

    ( [[ -f "/$skipflag" ]] || [[ -f "/tmp/${skipflag#.}" ]] ) && { RETURN $ret; }
    eth link $ETHLCFG_LAN '?' | grep up >/dev/null 2>&1
    ret=$?
# DEMO_MODE
    if [[ $ret -eq 0 ]] || [[ "$DEMO_MODE" == "true" ]]; then
        ret=0
        RETURN $ret
    fi
# DEMO_MODE END
    # no link found !!!
    waittime=$(udate)
    while [[ $ret -ne 0 ]]; do
        [[ "$((i%100000))" == "0" ]] && display "Network link not found"
        i=$((i+1))
        usleep $usleeptime
        eth link $ETHLCFG_LAN '?' | grep up >/dev/null 2>&1
        ret=$?
    done
    waittime=$(udate $waittime)
    display "Network link found after ${waittime#* }s"
    RETURN $ret
}

# removeTrailing
#--------------
# Remove all the characters corresponding to "item" that is at the end of "var"
# ARGS:
#   item    Can be a class or a list of characters (as in regexp)
#   var     The name of the var to clean
removeTrailing() {
    local FUNCTION=removeTrailing attr default
    ENTER_FUNCTION
    local item=$1 var=$2 \

    [ -z "$item" -o -z "$var" ] && { RETURN 1; }
    case "$item" in
        '[:space:]'|'[:blank:]') : "${attr}space/blank${default}"
            item="[[:graph:]]"
        ;;
        \[:*:\]) : "${attr}class other than space/blank${default}"
            item="[^[:${item#\[:}]"                                 # to restore gvim highlight }"
        ;;
        *) : "${attr}list of values${default}"
            item="[^$item]"
        ;;
    esac
    eval "$var=\${$var%\${$var##*$item}}"
    RETURN 0
}

# removeLeading
#--------------
# Remove all the characters corresponding to "item" that is at the begining of "var"
# ARGS:
#   item    Can be a class or a list of characters (as in regexp)
#   var     The name of the var to clean
removeLeading() {
    local FUNCTION=removeLeading attr default
    ENTER_FUNCTION
    local item=$1 var=$2 \

    [ -z "$item" -o -z "$var" ] && { RETURN 1; }
    case "$item" in
        '[:space:]'|'[:blank:]') : "${attr}space/blank${default}"
            item="[[:graph:]]"
        ;;
        \[:*:\]) : "${attr}class other than space/blank${default}"
            item="[^[:${item#\[:}]"                                 # to restore gvim highlight }"
        ;;
        *) : "${attr}list of values${default}"
            item="[^$item]"
        ;;
    esac
    eval "$var=\${$var#\${$var%%$item*}}"
    RETURN 0
}

# removeFromFifo
#---------------
# Remove all occurence of "item" from a "fifo" variable
# ARGS:
#   item    The item to remove
#   fifo    The variable that stores the fifo
removeFromFifo() {
    local FUNCTION=removeFromFifo attr default
    ENTER_FUNCTION
    local item=$1 fifo=$2 \
          IFS=^

    eval "$fifo=\"$IFS\$$fifo$IFS\" $fifo=\"\${$fifo//$IFS$item$IFS/$IFS}\" $fifo=\"\${$fifo//$IFS$IFS}\" $fifo=\"\${$fifo%%$IFS}\" $fifo=\"\${$fifo##$IFS}\""
    RETURN 0
}

# push
#-----
# Push values into a "fifo" variables
# ARGS:
#   fifo                The "fifo" variable
#   item1 [item2] ...   The items to push into the "fifo" variable
push() {
    local FUNCTION=push attr default
    DEFAULT_DEBUG_OFF
    ENTER_FUNCTION
    local fifo=$1 \
          citems nitems IFS

    shift
    [[ $# -eq 0 ]] && { RETURN 1; }
    IFS=^
    eval "citems=\"\${$fifo+\$$fifo$IFS}\" nitems='$*' $fifo=\"\$citems\$nitems\""
    RETURN 0
}

# pop
#----
# Pop an item from the "fifo" variable and store it in "var"
# ARGS:
#   var     The variable that will hold the poped item
#   fifo    The "fifo" variable
pop() {
    local FUNCTION=pop attr default
    DEFAULT_DEBUG_OFF
    ENTER_FUNCTION
    local varname=$1 fifo=$2 \
          ret=0 IFS=^

    eval "set \${$fifo:-\"\"}; $varname=\$1; shift; $fifo=\"\$*\";[ -n \"\$$varname\$$fifo\" ] && ret=0 || ret=1"
    RETURN $ret
}

# _ADD_POPALL
# popall
#-------
# Pop all the items of the "fifo" variable and display them on the stdout
# ARGS:
#   fifo    The "fifo" to empty
popall() {
    local FUNCTION=popall attr default
    DEFAULT_DEBUG_OFF
    ENTER_FUNCTION
    local fifo=$1 \
          ret=0 val= IFS=^

    eval "set \${$fifo:-\"\"}; while [[ \$# -ne 0 ]]; do [[ \${1//[[:blank:]]} != \$1 ]] && val=\"\$val \\\"\$1\\\"\" || val=\"\$val \$1\"; shift; done; $fifo="
    echo $val
    RETURN $ret
}
# _ADD_POPALL END

# definedMac
#-----------
# Check the format of the "mac" address
# ARGS:
#   mac     The mac address to check
definedMAC() {
    local FUNCTION=definedMAC attr default
    ENTER_FUNCTION
    local MAC=${1//[:-]} \
          ret=0

    [[ -n "${MAC//[[:xdigit:]]}" ]] || [[ ${#MAC} -ne 12 ]] || [[ "$((0x$MAC))" == "0" ]] && ret=1
    RETURN $ret
}

# CRMS00286717
# echolog
#----------
# Display the "msg" in stderr and log it
# ARGS:
#   prefix      The prefix to use
#   errormsg    The error message to display
# NOTE:
# - if "logattr" attribute is set, use it to display the error message and revert to "default" attribute
echolog() {
    local FUNCTION=echolog
    ENTER_FUNCTION
    local prefix=$1 attr default RET=0 logger_args=
    shift

    echo "${logattr:-}$prefix: $*${default:-}" >&2
    [[ -n "$scriptname" ]] && logger_args="-t $scriptname"
#PR411913 tingh
    logger $logger_args -p local1.info "$prefix: $*" & wait $!
#PR411913 tingh END    
	RET=$?
    RETURN $RET
}

# echoerror
#----------
# Display the "errormsg" in stderr and log it
# ARGS:
#   errormsg    The error message to display
# NOTE:
# - if "errorattr" attribute is set, use it to display the error message and revert to "default" attribute
echoerror() {
    local FUNCTION=echoerror attr default logger_args=
    ENTER_FUNCTION
#PR411913 tingh
    local logattr prefix1="${PID:+($PID)}ERROR" prefix2=$1
    shift
    logattr="${errorattr:-}"
    echo "${logattr:-}$prefix1: $prefix2: $*${default:-}" >&2

    [[ -n "$scriptname" ]] && logger_args="-t $scriptname"
    logger $logger_args -p local1.emerg "$prefix1: $prefix2: $*" & wait $!
#PR411913 tingh END
    RETURN $?
}
# CRMS00286717 END

# is_ip
#------
# Check that the given "ip" is really an ip address
# ARGS:
#   ip  The ip address to check
is_ip() {
    local FUNCTION=is_ip attr default
    ENTER_FUNCTION
# CRMS00258266
    local ip="$1" ret
# CRMS00258266 END

    [[ -z "$ip" ]] && { RETURN 1; }
    nslookup $ip 0.0.0.0 >/dev/null 2>&1
# CRMS00258266
    ret=$?
    RETURN $ret
# CRMS00258266 END
}

# check_ip
#---------
# Check that:
# - the given "ip" & "netmask" are not null
# - the "netmask" is areal netmask
# - the "ip" is not of D class
# - the "ip" is neither 255.255.255.255 nor the subnet address nor the broadcast address
# ARGS:
#   ip          The ip address to check
#   netmask     The netmask to check
check_ip() {
    local FUNCTION=check_ip attr default
    ENTER_FUNCTION
# CRMS00258266
# CRMS00253434
    local ip=$1 netmask=$2 \
          tmpip notnetmask IFS ret
# CRMS00253434 END
# CRMS00258266 END

    : "${attr}check parameters${default}"
    [[ "__${ip}__" == "____" ]] && { RETURN 1; }

    if [[ "__${netmask}__" != "____" ]]; then
        : "${attr}check the mask${default}"
# CRMS00258266
        check_netmask $netmask || { ret=$?; RETURN $((10+$ret)); }
# CRMS00258266 END

        netmask=$((0x$(IFS="."; set $netmask; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))
        notnetmask=$((~netmask & 0xffffffff))
    fi

    : "${attr}transform string into a long${default}"
    ip=$((0x$(IFS="."; set $ip; [[ $# -ne 4 ]] && { echo 0; RETURN 2; }; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))

    : "${attr}check class D${default}"
    [[ "$((ip>>24))" != "$((0xe))" ]] || { RETURN 3; }

    : "${attr}check forbidden value: 0${default}"
    [[ "$ip" != "0" ]] || { RETURN 4; }

    : "${attr}check forbidden value: 255.255.255.255${default}"
    tmpip=$((0x$(IFS="."; set 255.255.255.255; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))
    [[ "$ip" != "$tmpip" ]] || { RETURN 5; }

    if [[ "__${netmask}__" != "____" ]]; then
        : "${attr}check forbidden value: subnet address${default}"
        [[ "$ip" != "$((ip&netmask))" ]] || { RETURN 6; }

        : "${attr}check forbidden value: broadcast address${default}"
        [[ "$((ip & notnetmask))" != "$notnetmask" ]] || { RETURN 7; }
    fi

    RETURN 0
}


# check_netmask
#--------------
# Check that the given "netmask" is a real netmask (neither 0.0.0.0 nor 255.255.255.255 are accepted)
# ARGS:
#   netmask     The netmask to check
check_netmask() {
    local FUNCTION=check_netmask attr default
    ENTER_FUNCTION
# CRMS00253434
    local mask=$1 _mask tmpmask IFS
# CRMS00253434 END

    : "${attr}check the parameters${default}"
    [ "__${mask}__" == "____" ] && { RETURN 1; }

    : "${attr}get the binary version${default}"
    mask=$((0x$(IFS="."; set $mask; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))
    _mask=$((-mask & 0xffffffff))

    : "${attr}check mask format${default}"
    [[ "$((_mask & mask ))" == "$_mask" ]] || { RETURN 2; }

    : "${attr}check forbidden value: 0.0.0.0 (rfc1878)${default}"
    [[ "$mask" != "0" ]] || { RETURN 3; }

    : "${attr}check forbidden value: 255.255.255.255 (phone only network is stupid)${default}"
    tmpmask=$((0x$(IFS="."; set 255.255.255.255; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))
    [[ "$mask" != "$tmpmask" ]] || { RETURN 4; }

    RETURN 0
}

# check_router
#-------------
# Check that the "router", "ip" and "netmask" are compatible.
# ARGS:
#   router      The router ip address to check
#   ip          The ip to check
#   netmask     The netmask to check
check_router() {
    local FUNCTION=check_router attr default
    ENTER_FUNCTION
# CRMS00253434
# CRMS00258266
    local router=$1 ip=$2 mask=$3 IFS ret
# CRMS00258266 END
# CRMS00253434 END

    : "${attr}check the parameters${default}"
    [ "__${router}__" == "____" ] && { RETURN 1; }
    [ "__${ip}__" == "____" ] && { RETURN 2; }
    [ "__${mask}__" == "____" ] && { RETURN 3; }

    : "${attr}check the mask${default}"
# CRMS00258266
    check_netmask $mask || { ret=$?; RETURN $((10+$ret)); }
# CRMS00258266 END

    : "${attr}get binary version of the args${default}"
    router=$((0x$(IFS="."; set $router; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))
    ip=$((0x$(IFS="."; set $ip; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))
    mask=$((0x$(IFS="."; set $mask; printf "%02x%02x%02x%02x" $1 $2 $3 $4)))

    : "${attr}IP != ROUTER ?${default}"
    [[ "$ip" != "$router" ]]  || { RETURN 4; }

    : "${attr}IP and ROUTER are in the same subnet ?${default}"
    [[ "$((ip&mask))" == "$((router&mask))" ]] || { RETURN 5; }

    RETURN 0
}

# CRMS00312748
# getPID
#-------
# get into 'PID' the real pid of the process and not just '$$'
getPID()
{
    local FUNCTION=getPID attr default
    ENTER_FUNCTION
    local tmp
    read </proc/self/stat PID tmp 2>/dev/null
    RETURN 0
}

# mutex_trylock
#--------------
# try to lock a mutex
# ARGS:
#   mutex       The mutex name
#   namespace   Optional, use the scriptname if defined otherwise use basename of '$0'
# RETURN VALUE:
#   0           The mutex has been successfully locked
#   1           The mutex could not be locked   
#   2           No mutex context found
mutex_trylock() {
    local FUNCTION=mutex_trylock attr default
    ENTER_FUNCTION
# PR341502 zimingxu+
    local mutex="$1" namespace="${2:-${scriptname:-${0##*/}}}" caller_pid tmp_other
# PR341502 zimingxu-
    local ret=1 owner="" locked=1

# PR341502 zimingxu+
    read </proc/self/stat caller_pid tmp_other 2>/dev/null
# PR341502 zimingxu-
    
    # try to lock the mutex
    mkdir 2>/dev/null -m 0700 -p  $mutexdir/$namespace/$mutex
    if mkfifo 2>/dev/null $mutexdir/$namespace/$mutex/waitqueue 2>&1; then
# PR341502 zimingxu+
        echo $caller_pid >$mutexdir/$namespace/$mutex/owner
# PR341502 zimingxu-
        : "${attr}locking succeded${default}"
        locked=0
        ret=0
    elif read 2>/dev/null <$mutexdir/context/$mutex/owner owner; then
        : "${attr}succeed if we are the owner of $mutex${default}"
# PR341502 zimingxu+
        [[ "$owner" == "$caller_pid" ]] && ret=0 || ret=1
# PR341502 zimingxu-
    else
        ret=1
    fi
    if [[ "$namespace" != "context" ]] && [[ ! -p $mutexdir/context/$namespace/waitqueue ]]; then
        : "${attr}no context${default}"
        ret=2
        if [[ $locked -eq 0 ]]; then
            : "${attr}free the lock${default}"
            exec 3<>$mutexdir/$namespace/$mutex/waitqueue
            rm -rf $mutexdir/$namespace/$mutex
            exec 3<&- 3>&-
        fi
    fi
    RETURN $ret
}

# mutex_lock
#-----------
# Block until the mutex is own by the process. If it is already ours return as if the lock was successfull.
# ARGS:
#   mutex       The mutex name
#   namespace   Optional, use the scriptname if defined otherwise use basename of '$0'
# RETURN VALUE:
#   2           No mutex context found
mutex_lock() {
    local FUNCTION=mutex_lock attr default
    ENTER_FUNCTION
# PR341502 zimingxu+
    local mutex="$1" namespace="${2:-${scriptname:-${0##*/}}}" caller_pid tmp_other
# PR341502 zimingxu-
    local ret=1 owner="" locked=1

# PR341502 zimingxu+
    read </proc/self/stat caller_pid tmp_other 2>/dev/null
# PR341502 zimingxu-

    while :; do
        read 2>/dev/null <$mutexdir/$namespace/$mutex/waitqueue ret
        mkdir -m 0700 -p  $mutexdir/$namespace/$mutex 2>/dev/null
        mkfifo 2>/dev/null $mutexdir/$namespace/$mutex/waitqueue && break || : "${attr}wait for $mutexdir/$namespace/$mutex/waitqueue${default}"
    done
# PR341502 zimingxu+
    echo $caller_pid > $mutexdir/$namespace/$mutex/owner
# PR341502 zimingxu-
    : "${attr}locking succeded${default}"
    ret=0
    if [[ "$namespace" != "context" ]] && [[ ! -p $mutexdir/context/$namespace/waitqueue ]]; then
        : "${attr}no context${default}"
        ret=2
        : "${attr}free the lock${default}"
        exec 3<>$mutexdir/$namespace/$mutex/waitqueue
        rm -rf $mutexdir/$namespace/$mutex
        exec 3<&- 3>&-
    fi
    RETURN $ret
}

# mutex_unlock
#-------------
# unlock a mutex if it is ours, otherwise do nothing
# ARGS:
#   mutex       The mutex name
#   namespace   Optional, use the scriptname if defined otherwise use basename of '$0'
# RETURN VALUE:
#   0           The mutex has been successfully unlocked
#   1           The mutex is not ours to unlock
mutex_unlock() {
    local FUNCTION=mutex_unlock attr default
    ENTER_FUNCTION
# PR341502 zimingxu+
    local mutex="$1" namespace="${2:-${scriptname:-${0##*/}}}" caller_pid tmp_other
# PR341502 zimingxu-
    local ret=1 owner=""
    
# PR341502 zimingxu+
    read </proc/self/stat caller_pid tmp_other 2>/dev/null
# PR341502 zimingxu-

    [[ -z "$mutex" ]] && { RETURN $ret; }
    : "${attr}our mutex ?${default}"
    read 2>/dev/null <$mutexdir/$namespace/$mutex/owner owner
# PR341502 zimingxu+
    if [[ "${owner:-1}" == "$caller_pid" ]]; then
# PR341502 zimingxu-
        : "${attr}yes, unlocking it${default}"
        exec 3<>$mutexdir/$namespace/$mutex/waitqueue
        mv $mutexdir/$namespace/$mutex $mutexdir/$namespace/$mutex.to_remove
        rm -rf $mutexdir/$namespace/$mutex.to_remove
        exec 3<&- 3>&-
        ret=0
    else
        ret=1
    fi
    RETURN $ret
}

# mutex_create_context
#---------------------
# initialise mutex context for the script
# ARGS:
#   namespace   Optional, use the scriptname if defined otherwise use basename of '$0'
# NOTE:
#   2 mutexes are needed,
#       - the 1st 'sync_${namespace}' is the own to synchonize all concurrent access to the mutex_create_context,
#       it is released at the end of this function
#       - the 2nd '${namespace}' is the mutex that will be release at the end of mutex_delete_context
mutex_create_context() {
    local FUNCTION=mutex_create_context attr default
    ENTER_FUNCTION
# PR341502 zimingxu+
    local namespace="${1:-${scriptname:-${0##*/}}}" caller_pid tmp_other
# PR341502 zimingxu-
    local mutex sync_mutex ret=1 mx owner contextdir

# PR341502 zimingxu+
    read </proc/self/stat caller_pid tmp_other 2>/dev/null
# PR341502 zimingxu-

    mutex=${namespace} sync_mutex=sync_${namespace} contextdir=$mutexdir/context

    mkdir 2>/dev/null -m 0700 -p $contextdir
    
    if [[ -p $contextdir/$sync_mutex/waitqueue ]]; then
        ### TODO: possible race condition here !!!!
        if read 2>/dev/null <$contextdir/$sync_mutex/owner owner; then
            # a sync_mutex exists, assure that it is not a dead one 
            [[ ! -d /proc/${owner:-0} ]] &&  rm -rf $contextdir/$sync_mutex 2>/dev/null
        else
            : "${attr}mutex $sync_mutex has no owner${default}"
            exec 3<>$contextdir/$sync_mutex/waitqueue
            mv $contextdir/$sync_mutex $contextdir/$sync_mutex.to_remove
            rm 2>/dev/null -rf $contextdir/$sync_mutex.to_remove
            exec 3<&- 3>&-
        fi
    fi
    : "${attr}locking $sync_mutex${default}"
    mkdir -m 0700 -p $contextdir/$sync_mutex 2>/dev/null
    mutex_lock $sync_mutex context

    # cleanup mutex context
    if read 2>/dev/null <$contextdir/$mutex/owner owner; then
        : "${attr}A context has been found${default}"
        if [[ ! -d /proc/${owner:-0} ]]; then
            : "${attr}=====================${default}"
            : "${attr}removing dead context${default}"
            : "${attr}=====================${default}"
            exec 3<>$contextdir/$mutex/waitqueue
            mv $contextdir/$mutex $contextdir/$mutex.to_remove
            exec 4<>$mutexdir/$mutex/waitqueue
            mv $mutexdir/$mutex $mutexdir/$mutex.to_remove
            rm 2>/dev/null -rf $contextdir/$mutex.to_remove $mutexdir/$mutex.to_remove
            exec 3<&- 3>&- 4<&- 4>&-
        else
            # check all other mutexes
            for mx in $(\ls -d $mutexdir/$mutex/* 2>/dev/null); do
                read 2>/dev/null <$mx/owner owner
# PR341502 zimingxu+
                [[ "$owner" == "$caller_pid" ]] && [[ "${mx//$sync_mutex}" != "$mx" ]] && mutex_unlock ${mx##*/}
# PR341502 zimingxu-
                [[ ! -d /proc/${owner:-0} ]] && rm -rf $mx
            done
        fi
    fi
    # ensure that the mutex dir exists
    if mutex_trylock $mutex context; then
        mkdir 2>/dev/null -m 0700 -p $mutexdir/$mutex
    fi

    # unlock sync_mutex
    mutex_unlock $sync_mutex context
    ret=0
    RETURN $ret
}

# mutex_delete_context
#---------------------
# uninitialise mutexes for the script
# ARGS:
#   namespace   Optional, use the scriptname if defined otherwise use basename of '$0'
mutex_delete_context() {
    local FUNCTION=mutex_delete_context attr default
    ENTER_FUNCTION
# PR341502 zimingxu+
    local namespace="${1:-${scriptname:-${0##*/}}}" caller_pid tmp_other
# PR341502 zimingxu-
    local mutex sync_mutex ret=1 mx owner contextdir

# PR341502 zimingxu+
    read </proc/self/stat caller_pid tmp_other 2>/dev/null
# PR341502 zimingxu-

    mutex=${namespace} sync_mutex=sync_${namespace} contextdir=$mutexdir/context

    # clean up all our mutexes that are still locked
    for mx in $(\ls -d $mutexdir/$mutex/* 2>/dev/null); do
        read 2>/dev/null <$mx/owner owner
# PR341502 zimingxu+
        [[ "$owner" == "$caller_pid" ]] && mutex_unlock ${mx##*/}
# PR341502 zimingxu-
    done

    # wait for the other if we are the owner of the context mutex
    read 2>/dev/null <$mutexdir/context/$mutex/owner owner
# PR341502 zimingxu+
    if [[ "${owner:-0}" == "$caller_pid" ]]; then
# PR341502 zimingxu-
        # lock sync mutex
        mutex_lock $sync_mutex context
        # wait for all mutexes that are not ours to be freed before cleaning things
        while :; do
            mx="$(ls -d $mutexdir/$mutex/* 2>/dev/null | head -n 1)"
            [[ -z "$mx" ]] && break
            read 2>/dev/null <$mx/owner owner
# PR341502 zimingxu+
            if [[ "$owner" == "$caller_pid" ]]; then
# PR341502 zimingxu-
                mutex_unlock ${mx##*/}
            elif [[ ! -d /proc/${owner:-0} ]]; then
                # unlock the orphan mutex
                [[ -p $mx/waitqueue ]] && exec 3<>$mx/waitqueue || exec 3<>/dev/null
                rm 2>/dev/null -rf $mx
                exec 3<&- 3>&-
            else
                : "${attr}waiting for $mx/waitqueue${default}"
                read 2>/dev/null <$mx/waitqueue ret
            fi
        done
        # all mutexes have been released freeing everything
        rmdir $mutexdir/$mutex 2>/dev/null
        # releasing 
        mutex_unlock $mutex context
        mutex_unlock $sync_mutex context
    fi
    ret=0
    RETURN $ret
}
# CRMS00312748 END

# CRMS00351686
# parseUrl   
#---------------
# IN:
#   full URL
# OUT:
#   parseUrl_url (URL without protocol)
#   parseUrl_proto (protocol)
#   parseUrl_server (server IP or name)
#   parseUrl_port
#   parseUrl_path
parseUrl()
{
    local FUNCTION=parseUrl attr default
    ENTER_FUNCTION

    # extract the protocol
    parseUrl_proto="$(echo $1 | grep :// | sed -e's,^\(.*://\).*,\1,g')"

    # remove the protocol
    if [[ ! -z "$parseUrl_proto" ]];then
      parseUrl_url="$(echo $1 | sed -e s,$parseUrl_proto,,g)"
      parseUrl_proto="$(echo $parseUrl_proto | sed -e 's,://,,g')"
    else
      parseUrl_url="$1"
    fi

    # locate 1st '/' and replace it by 'space'
    parseUrl_url="$(echo $parseUrl_url | sed -e 's,/, ,1')"
    # then get path if found
    parseUrl_path="/$(echo $parseUrl_url | grep ' ' | cut -d' ' -f2)"
    if [[ "$parseUrl_path" != "/" ]]; then
       parseUrl_url="$(echo $parseUrl_url | grep ' ' | cut -d' ' -f1)"
    fi

    # split server & port if any
    parseUrl_server="$(echo $parseUrl_url | grep : | cut -d: -f1)"
    if [[ ! -z "$parseUrl_server" ]]; then
      # port is present
      parseUrl_port="$(echo $parseUrl_url | grep : | cut -d: -f2 | sed -e's,/.*,,g')"
    else
      # no port 
      parseUrl_port=""
      parseUrl_server="$parseUrl_url"
    fi
    
    : "${attr}====================================================================${default}"
    : "${attr}==== Parsing URL: [$1]                       ${default}"
    : "${attr}==== parseUrl_proto=[$parseUrl_proto]        ${default}"
    : "${attr}==== parseUrl_server=[$parseUrl_server]      ${default}"
    : "${attr}==== parseUrl_path=[$parseUrl_path]          ${default}"
    : "${attr}==== parseUrl_port=[$parseUrl_port]          ${default}"
    : "${attr}====================================================================${default}"

    RETURN 0

}
# CRMS00351686 END
