# 24/08/98  Modification to search first in the NETWORK directory then in the NET80 directory
#           for LISTENER.ORA and TNSNAMES.ORA files.
#           Also added extra procs to help distinguish between v7 and v81 listeners.
# 17/08/98  Remove ENVS variable from BEQ connect descriptor as it is not used on NT.
# 06/09/98  Add Bequeath Connect descriptor
# 05/21/98  modified dbEntry format to accept FailSafe discovery.
# 01/10/97  fixed bug#593722, the agent now looks in sqlnet.ora for the DefaultDomain.
# 12/18/97  syetchin Bug fix (#602576) MOH - autodiscovery problem for discovering Listeners
# 12/08/97  syetchin Bug fix (#564771) TNS_ADMIN support for finding lister.ora and tnsnmes.ora
#
# 01/12/97  added extra error checking and fix bug associated with agent NOT starting if NO DBs exist.
# 26/11/97  mbyrne  added } which prevented agent starting.
# 26/11/97  mbyrne  appended *.world to the end of each database name if it
#           does not already exits
# 26/11/97  mbyrne  changed to support MOH we will now look for the default ohome
#       (ie the home where the agent resides), based on the service name passed to the agent.
# 26/11/97  mbyrne  added extra checking around regfind,
#           removed lang dependancy.
# 26/11/97  syetchin Implemented MOH
# 25/11/97  mbyrne  appended *.world to the end of each database name if it
#           does not already exits.
#
# 10/10/97  mbyrne  add ORACLE_HOME & SERVER to the connect string
#                   of the database. This fixed an MTS bug.
#
# 04/14/97  mbyrne
# removed reference to registery.dll and dependant functions.
#
# 11/12/1996    mbyrne
# changed network to net30 to conform with Version 8.0.2
#
#   21\11\96    mbyrne
#   This file is for use by the Agent on startup only.
#   It is not a script to be run from OEM, bug#419039.
#
#
# copyright (c) 1995 by the Oracle Corporation

set Parameters(ORACLE_NODE) {ServiceType HostName NodeAddress NodeUserData}
set Parameters(ORACLE_LISTENER) {ServiceType HostName ServiceAddress}
set Parameters(ORACLE_DATABASE) {ServiceType HostName ServiceAddress Listener}
global nmiconf_traceProcList
set nmiconf_traceProcList {}

# Global list for discovered service names
# new -- ServiceNames is now a list, most of whose members are themselves
# lists with two elements, a service name and the type of that service.
# Some members will still simply be service-name strings, generated by
# pre-8.1.3 third-party discovery scripts
set ServiceNames {}

############## DEFINE PROCS

#error handling

# Logs the specified message
proc nmiconf_log {message} {
    global nmiconf_logfile
    set fd [open $nmiconf_logfile "a+"]
    puts $fd "$message"
    close $fd
}

# Provided to allow for debugging trace information. Called with the string that
# is specified.
#
# If procedure names are specified, the call frame is indicated to reflect
# potential nesting of calls and is contained within the message traced.
#
proc nmiconf_trace {message} {
    global nmiconf_traceEnabled
    if {$nmiconf_traceEnabled} {
        global nmiconf_traceProcList
        if {[llength $nmiconf_traceProcList] > 0} {
            nmiconf_log "\[[llength $nmiconf_traceProcList]\][lindex $nmiconf_traceProcList 0]: $message"
        } else {
            nmiconf_log "\[0\]<main>: $message"
        }
    }
}

# Used at the beginning of a function, nmiconf_beginProcTrace indicates the
# function that is being traced. All subsequent nmiconf_trace statements
# are prefixed with the procedure name.
#
# Callers are expected to call nmiconf_endProcTrace to remove the function
# from the prefix list
proc nmiconf_beginProcTrace {procedure} {
    global nmiconf_traceEnabled
    if {$nmiconf_traceEnabled} {
        global nmiconf_traceProcList

        # Insert the procedure name to the head of the list
        set nmiconf_traceProcList [linsert $nmiconf_traceProcList 0 $procedure]
    }
}

# Used at the end of a function being traced, nmiconf_endProcTrace removes the
# procedure name from the list of functions being traced.
#
# Callers are expected to have called nmiconf_beginProcTrace first.
proc nmiconf_endProcTrace {procedure} {
    global nmiconf_traceEnabled
    if {$nmiconf_traceEnabled} {
        global nmiconf_traceProcList

        # Verify the head of the nmiconf_traceProcList is indeed the function
        # specified.
        if {[llength $nmiconf_traceProcList] <= 0} {
            nmiconf_trace "TRACING ERROR - end of funcion without begin detected!"
        } elseif {[string compare [lindex $nmiconf_traceProcList 0] $procedure] != 0} {
            nmiconf_trace "TRACING ERROR - attempted end trace for $procedure when [lindex $nmiconf_traceProcList 0] is active."
        } else {
            set nmiconf_traceProcList [lrange $nmiconf_traceProcList 1 end]
        }
    }
}

# Logs the specified message as warning
proc nmiconf_warning {message} {
    nmiconf_log "Warning : $message"
}

# Logs the specified message as error
# Also terminates the control of the calling thread unless catched
proc nmiconf_error {message} {
    nmiconf_log "Error : $message"
    error $message
}

# Reads a (logical) line from a nlpa file. A logical line is a (name=value) pair
# Example of nlpa files are listener.ora, tnsnames.ora and sqlnet.ora
# Note that a logical line can span over multiple physical lines in the file.
# Returns a list {key value}
proc nmiconf_getOraLine {file key value} {
  set InAKey 0
  set InAValue 0
  upvar $key Key
  upvar $value Value
    while {[set ret [gets $file line]] >= 0} {
        set pound [string first # $line]
        if {$pound >= 0} {
            set line [string range $line 0 [expr $pound - 1]]
        }
        set line [string trim $line]
        set length [string length $line]
        if {($length <= 0)} {continue}
        if {($InAKey == 0) && ($InAValue == 0)} {
            set InAKey 1
            set LParens 0
            set RParens 0
            set Value ""
        }
        if {($InAKey == 1)} {
            for {set i 0} {($i < $length) && ([string index $line $i] != "=")} {incr i} {}
            set Key [string trim [string range $line 0 [expr $i - 1]]]
            # new: if there are multiple keys, we only need the first.
            # (for tnsnames.ora)
            set Key [lindex [split $Key ","] 0]
        set line [string range $line [expr $i + 1] end]
            set line [string trim $line]
            set length [string length $line]
            set InAKey 0
            set InAValue 1
        }
        if {($length <= 0)} {continue}
        if {($InAValue == 1)} {
            append Value $line
            for {set i 0} {$i < $length} {incr i} {
                if {[string index $line $i] == "("} {
                    incr LParens
                }
                if {[string index $line $i] == ")"} {
                    incr RParens
                }
            }
            if {$LParens == $RParens} {
                set InAnAddress 0
                break
            }
        }
  }
  return $ret
}

# Read through a sqlnet config file, appending its logical lines to
# a list, each of whose entries is a two-member list of a key and value:
# { {key1 value1} {key2 value2} {key3 value3} ... }
# If we encounter an IFILE, pursue it and add its lines to the same list,
# in the position where the IFILE was encountered.
proc nmiconf_readNetConfigFile { filename kvlist } {

    upvar $kvlist kvlist_loc

    if {![file exists $filename]} {
    nmiconf_warning "$filename does not exist"
    return
    }

    if { [catch {set fileHdl [open $filename r]} msg] } {
    nmiconf_warning "Error while opening $filename : $msg"
    return
    }
    while {[nmiconf_getOraLine $fileHdl key value] >= 0} {
        if {[regexp -nocase "ifile" $key]} {
        nmiconf_readNetConfigFile $value kvlist_loc
        continue;
    }
    lappend kvlist_loc [list $key $value]
    }
    close $fileHdl
}


# Reads through a tnsnames.ora, loading the entries into the lookup-table tnsnames
# Notes
# 1) Uses the following globals
# dbalias_list -- list of alias names { alias1, alias2 ... aliasN }
# tnsnames -- table containing the aliases. The alias name is used as index of the table.
# tnsnames(alias1) = value of alias1
# tnsnames(alias2) = value of alias2
# ...
# tnsnames(aliasN) = value of aliasN
# 2) If a alias name doesn't contain the domain name, the agent's
# default domain is appended
proc nmiconf_readTnsnamesOra { names_file } {
    global tnsnames dbalias_list

    nmiconf_beginProcTrace "nmiconf_readTnsnamesOra"

    set kvlist {}
    nmiconf_trace "Reading in contents of tnsnames file: $names_file"
    nmiconf_readNetConfigFile $names_file kvlist

    foreach kv $kvlist {
        set key [lindex $kv 0]
        set value [lindex $kv 1]

        #This is to prevent the NT backlash problem from producing a zero length
        #services.ora, due to ORACLE_HOME param in listener.ora
        regsub -all {\\} $value {/} value
        if {![regexp "\[^\\\]\\." $key]} {
            append key [nmiconf_agentDefaultDomain]
        }

        if {![info exists tnsnames($key)]} {
            nmiconf_trace "located service $key, address = $value"
            set tnsnames($key) $value
            lappend dbalias_list $key
        } else {
            nmiconf_trace "$key has already been found in another TNSNAMES file,"
            nmiconf_trace "  using address == $tnsnames($key)"
        }
    }
    nmiconf_trace "[llength $dbalias_list] database aliases \"{$dbalias_list}\" recorded"
    nmiconf_endProcTrace "nmiconf_readTnsnamesOra"
}

# From 8.1 on, the definition of a listener in listener.ora may
# be a DESCRIPTION or DESCRIPTION_LIST instead of an ADDRESS or
# ADDRESS_LIST.  We still need an ADDRESS(_LIST) (so we can wrap
# it in a DESCRIPTION with CONNECT_DATA to make database connect
# descriptors), so we have to tear it out, now.
proc nmiconf_makeListenerAddress {description} {

    nmiconf_beginProcTrace "nmiconf_makeListenerAddress"

    # First, we need to get rid of any DESCRIPTION that has a
    # PROTOCOL_STACK definition that does *not* use TTC/TNS
    nmiconf_trace "Removing exotic protocol stacks from listener address"
    set TNSDescription [nmiconf_RemoveExoticProtocolStacks $description];

    # Next, we take any remaining TCP ADDRESSes out of what's left,
    # and string 'em together in an ADDRESS_LIST.
    # We need some regexps defined first.
    # whitespace
    set ws {[ ]*}
    # any one-paren-deep NV binding (we're in trouble if there are
    # extra levels of parentheses around)
    set paren {\([^\(\)]*\)}
    # the "(protocol = TCP)" NV
    set tcp "\\(${ws}PROTOCOL${ws}=${ws}TCP${ws}\\)"
    # so this should match any "(address = <whatever>(protocol=tcp)<whatever>)"
    set tcpaddressexp "\\(${ws}ADDRESS${ws}=${ws}(${paren}${ws})*${tcp}${ws}(${paren}${ws})*\\)"

    set TCPAddressList "(ADDRESS_LIST = "
    while {[regexp -nocase "($tcpaddressexp)(.*)" $TNSDescription whole TCPAddress trash1 trash2 rest]} {
        nmiconf_trace "adding listener address of \"$TCPAddress\""
        append TCPAddressList $TCPAddress
        set TNSDescription $rest
    }
    append TCPAddressList ")"

    nmiconf_endProcTrace "nmiconf_makeListenerAddress"
    return $TCPAddressList
}


# in order to get TNS TCP addresses without the IIOP TCP addresses,
# we need to read through a block of text, removing any DESCRIPTION
# (with balanced parens) that has a non-TTC/TNS PROTOCOL_STACK
proc nmiconf_RemoveExoticProtocolStacks {dlist} {

    while {[regexp -nocase -indices {\([ ]*DESCRIPTION[ ]*=[ ]*.*} $dlist whole]} {
    #First, figure out how long the body of the DESCRIPTION is
    set parenLevel 1;
        set start [lindex $whole 0]
        set length [string length $dlist];
    for {set i [expr $start + 1]} {($i < $length) && ($parenLevel)} {incr i} {
        switch -exact -- [string index $dlist $i] {
        "(" {incr parenLevel}
        ")" {incr parenLevel -1}
        }
    }
    set end [expr $i - 1]

    # now, either we're at the end of the string or we've balanced
    # our parentheses.  Whichever, we've got our DESCRIPTION.
    set description [string range $dlist $start $end]
    if {[regexp -nocase {\PROTOCOL_STACK[ ]*=[ ]*(.*)} $description protwhole protrest] && !([regexp -nocase {SESSION[ ]*=[ ]*NS} $protrest] &&[regexp -nocase {PRESENTATION[ ]*=[ ]*TTC} $protrest])} {
        set dlist "[string range $dlist 0 [expr $start - 1]][string range $dlist [expr $end + 1] $length]"
    } else {
        # hack -- Change DESCRIPTION to ESCRIPTION so it won't
            # be picked up again
        set dlist "[string range $dlist 0 $start][string range $dlist [expr $start + 2] $length]"
    }
    }

    return $dlist
}


# Read through a listener.ora, loading the properties of listeners into lookup-tables
# The algorithm does 2 passes of the file
# In first pass it collects the listener names.
# In second pass it identifies the SIDs monitored by the listeners.
#
# Notes
# 1) Uses the following globals
# listener_list -- list containing the unique names of the listeners
#   if 2 listener.ora files define a listener with the same name (say "LISTENER") a index
#   is suffixed (as LISTENER1) to make the name unique
#       The generated unique listener name is used as key to the following tables.
# listener_nameinconfig -- table containing the names of the listeners as specified in listener.ora file
# listener_address -- table containing the address info of the listeners
# listener_config -- table containing the full path name of the listener.ora file
# listener_sids -- table containing the SIDs monitored by the listeners.
# listener_ntservicename -- table containing the NT service name of the listeners
# ListenerSuppliedName -- table containing the GLOBAL_DBNAME of the listeners.

#
# Returns the modified listener_ntservicelist
# The NT service name entries corresponding to the listeners defined in listener.ora file are
# removed from listener_ntservicelist. The modified listener_ntservicelist is returned.
proc nmiconf_readListenerOra { listener_file listener_home listener_ntservicelist} {
    global listener_nameinconfig listener_address listener_config listener_sids listener_ntservicename
    global ListenerSuppliedName listener_oraclehome
    global listener_list

    set kvlist {}
    nmiconf_beginProcTrace "nmiconf_readListenerOra"
    nmiconf_trace "listener_file=$listener_file, listenerOraHome=$listener_home"
    nmiconf_readNetConfigFile $listener_file kvlist

    # First pass
    nmiconf_trace "Parsing contents of $listener_file file, pass 1 looking for \"(ADDRESS\" in records"
    foreach kv $kvlist {
        set key [lindex $kv 0]
        set value [lindex $kv 1]

        #This is to prevent the NT backlash problem from producing a zero length
        #services.ora, due to ORACLE_HOME param in listener.ora
        regsub -all {\\} $value {/} value
        if {![regexp -nocase "\\(\[ \]*ADDRESS" $value]} {
            continue
        }

        # Check if the listener is actually configured to listen
        # on this host!!!
        if {![nmiconf_isThisHostAddress $value]} {
            nmiconf_trace "Skipping the address as it is not for this host, \"$value\""
            continue
        }

        # Find the NT service name of the listener
        nmiconf_trace "Located TNS address descriptor for listener for this host, continuing..."
        if {[nmiconf_isTNSHome $listener_home]} {
            # In TNS_ADMIN case, first look for a matching Network service name, then look for
            # Pre-Network (NET80) service name
            nmiconf_trace "Comparing listener against various version of listener name format"
            set servicePos [nmiconf_findListenerNTService $key [nmiconf_Listener80Version] $listener_ntservicelist]
            if { $servicePos == -1 } {
                set servicePos [nmiconf_findListenerNTService $key [nmiconf_Listener81Version] $listener_ntservicelist]
                if { $servicePos == -1 } {
                    set servicePos [nmiconf_findListenerNTService $key [nmiconf_Listener73Version] $listener_ntservicelist]
                }
            }
        } else {
            # In home case, get version from listener.ora file location
            # Use the version and name to locate the corresponding NT service name
            nmiconf_trace "TNS_ADMIN not defined for this listener, looking in oracle home at filename to determine version"
            set listenerVersion [nmiconf_getListenerOraFileVersion $listener_home $listener_file]
            set servicePos [nmiconf_findListenerNTService $key $listenerVersion $listener_ntservicelist]
            # This could be a 73 so let's try that before giving up
            if {$servicePos == -1 && [string compare $listenerVersion 81] == 0 } {
                 set servicePos [nmiconf_findListenerNTService $key [nmiconf_Listener73Version] $listener_ntservicelist]
            }
        }

        if {$servicePos == -1} {
            nmiconf_warning "Listener $key defined in $listener_file will be skipped because, it does not have a corresponding NT service entry"
            continue
        } else {
            set listenerNTServiceName [lindex $listener_ntservicelist $servicePos]
            set listener_ntservicelist [lreplace $listener_ntservicelist $servicePos $servicePos]
        }

        # since listener names are case-insensitive, everything
        # keyed off them should use lower-case names
        set lowerKey [string tolower $key];
        set listener_name [nmiconf_getUniqueListenerName $lowerKey]

        set listener_derivedname($lowerKey) $listener_name
        set listener_nameinconfig($listener_name) $key
        set listener_config($listener_name) $listener_file
        set listener_address($listener_name)  [nmiconf_makeListenerAddress $value]
        set listener_sids($listener_name) {}
        set listener_ntservicename($listener_name) $listenerNTServiceName
        set listener_oraclehome($listener_name) $listener_home
        nmiconf_trace "Temporarily storing information for listener \"$key\""
        nmiconf_trace "   unique Listener name: $listener_name"
        nmiconf_trace "   nameInConfig: $key"
        nmiconf_trace "   listenerConfigFile: $listener_file"
        nmiconf_trace "   listener Address: $listener_address($listener_name)"
        nmiconf_trace "   listener NTServiceName: $listenerNTServiceName"
        nmiconf_trace "   listener OracleHome: $listener_home"
    }

    # Start Second pass.
    nmiconf_trace "Pass 2 looking for \"SID_LIST\" for listener(s) in $listener_file"
    foreach kv $kvlist {
        set key [lindex $kv 0]
        set value [lindex $kv 1]

        # This is to prevent the NT backlash problem from producing a zero length
        # services.ora, due to ORACLE_HOME param in listener.ora
        regsub -all {\\} $value {/} value
        regsub -all "\t" $value "" value
        if {![regexp -nocase "SID_LIST_(.+)" $key whole lsnr] ||
                ([lsearch -exact $listener_list [string tolower $lsnr]] == -1)} {
            continue;
        }
        set listener_name $listener_derivedname([string tolower $lsnr])
        while {[regexp -nocase "SID_DESC\[ \]*=\[ \]*((\\(\[^\\)]*\\))*)\\)(.*)" $value whole desc trash value]} {
            if {![regexp -nocase "SID_NAME\[ \]*=\[ \]*(\[^\\)\]+)\[ \]*\\)" $desc whole SID]} {
                continue
            }

            lappend listener_sids($listener_name) [string trim $SID " "]
            nmiconf_trace " - located SID for listener \"$listener_name\", SID == \"$SID\""
            if {[regexp -nocase "GLOBAL_DBNAME\[ \]*=\[ \]*(\[^\\)\]+)\[ \]*\\)" $desc whole dbname]} {
                nmiconf_trace "   global_gbname found for SID $SID, using $dbname"
                set ListenerSuppliedName([string toupper $SID]) [string trim $dbname " "]
            } elseif {[regexp -nocase "GLOBAL_NAME\[ \]*=\[ \]*(\[^\\)\]+)\[ \]*\\)" $desc whole dbname]} {
                nmiconf_trace "   global_name found for SID $SID, using $dbname"
                set ListenerSuppliedName([string toupper $SID]) [string trim $dbname " "]
            } else {
                nmiconf_trace " - no global name found for $SID"
            }
        }
    }
    nmiconf_endProcTrace "nmiconf_readListenerOra"
    return $listener_ntservicelist
}

# Generates a unique listener name for the given listener name.
# The listener_list global is updated with the new unique name.
# Notes
# 1) Uses the following globals
# listener_list -- list of unique listener names
# listener_samename_index_list -- is used to maintain the index count of listener names in
# listener_list. Sample values are provided below
# listener_list = { listener listener1 listener2 }
# listener_samename_index_list = {2 0 0}
proc nmiconf_getUniqueListenerName { listener_name } {
    global listener_list listener_samename_index_list

    set actualListenerName ""
    while {1} {
        set listenerPos [lsearch -exact $listener_list $listener_name]
        if {$listenerPos == -1} {
            lappend listener_list $listener_name
            lappend listener_samename_index_list 0
            break;
        } else {
            if {![string length $actualListenerName] } {
                set actualListenerName $listener_name
                set actualListenerPos $listenerPos
            }
            set listener_samename_index [lindex $listener_samename_index_list $actualListenerPos]
            incr listener_samename_index
            set listener_samename_index_list [lreplace $listener_samename_index_list $actualListenerPos $listenerPos $listener_samename_index]
            set listener_name $actualListenerName$listener_samename_index
        }
    }

    return $listener_name
}

# Parses the listener properties from NT service name
# Returns the list { listenerName listenerVersion listenerHomeName }
# With the introduction of V8.1, it became increasingly difficult to distinguish
# between v81 and v7 (pre-80) listeners. We rely on the file location and the NT Listener ServiceName
# to tell us the version of a listener.
# We get the OracleHomeName from the NT registry
# We get the Version from the NT Service Name
# In pre-80 the OracleHomeName is NULL; the Version is NULL
# In 80 the OracleHomeName is NOT NULL; the Version is 80
# In 81 the OracleHomeName is NOT NULL; the Version is NULL
#
proc nmiconf_getListenerNTServiceDetails { serviceName } {

    nmiconf_beginProcTrace "nmiconf_getListenerNTServiceDetails"
    nmiconf_trace "Comparing $serviceName against expression \"(Oracle)(\[A-za-z0-9_-\]*)(TNSListener)(80)?(\[A-za-z0-9_-\]*)\""
    if {![regexp {(Oracle)([A-za-z0-9_-]*)(TNSListener)(80)?([A-za-z0-9_-]*)} $serviceName whole prefix homeName suffix listenerVersion listenerName]} {
        nmiconf_endProcTrace "nmiconf_getListenerNTServiceDetails"
        error "Invalid NT service name $serviceName"
    }

    set serviceDetails {}

    if {![llength $listenerVersion]} {
        nmiconf_trace "No version located in ServiceName, assumed to be pre 8.0 or 8.1"
        # The version is NULL for both 81 and pre-80 versions of listener
        if {![llength $homeName]} {
            # The ORACLE_HOME_NAME is null only for pre-80 versions of listener
            nmiconf_trace "No homename found in listener name, version of listener is 7.3.x"
            set listenerVersion [nmiconf_Listener73Version]
        } else {
            nmiconf_trace "Homename of \"$homeName\" found in service name. Listener is 8.1"
            set listenerVersion [nmiconf_Listener81Version]
        }
    } else {
        nmiconf_trace "Version marker of \"$listenerVersion\" found in serviceName. Listener is 8.0.x"
        set listenerVersion [nmiconf_Listener80Version]
        if {![llength $homeName]} {
            set homeName [nmiconf_default80HomeName]
        }
    }

    if {![llength $listenerName]} {
        set listenerName [nmiconf_defaultListenerName]
        nmiconf_trace "ListenerName not retrieved from the serviceName. Defaulting to: $listenerName"
    } else {
        nmiconf_trace "ListenerName is $listenerName - from serviceName"
    }

    lappend serviceDetails $listenerName
    lappend serviceDetails $listenerVersion
    lappend serviceDetails $homeName

    nmiconf_endProcTrace "nmiconf_getListenerNTServiceDetails"
    return $serviceDetails
}

proc nmiconf_defaultListenerName {} {
    return LISTENER
}

proc nmiconf_default80HomeName {} {
    return DEFAULT_HOME
}

proc nmiconf_Listener81Version {} {
    return 81
}

proc nmiconf_Listener80Version {} {
    return 80
}

proc nmiconf_Listener73Version {} {
    return 73
}

# Return the OracleHome given the OracleHome Name
# This concept of an OracleHomeName associated with an
# OracleHome was introduced in 8.0.
#
proc nmiconf_getHomeFromOracleHomeName { homeName } {

    set allOracleHomesAndNames [nmiconf_getAllOracleHomes]

    foreach homeAndNameEntry $allOracleHomesAndNames {
        if {[string match [lindex $homeName 0] [lindex [lindex $homeAndNameEntry 1] 0]]} {
            return [lindex $homeAndNameEntry 0]
        }
    }
}

# Return the OracleHomeName given the OracleHome
proc nmiconf_getHomeNameFromOracleHome { oracleHome } {

    set allOracleHomesAndNames [nmiconf_getAllOracleHomes]

    foreach homeAndNameEntry $allOracleHomesAndNames {
        if {[string match [lindex $oracleHome 0] [lindex [lindex $homeAndNameEntry 0] 0]]} {
            return [lindex $homeAndNameEntry 1]
        }
    }
}

# Infers the listener version from the specified listener.ora file name.
# The Agent must look in the NETWORK directory before the NET80 for 8.1.3
proc nmiconf_getListenerOraFileVersion { listener_home listenerOraFile } {

    nmiconf_beginProcTrace "nmiconf_getListenerOraFileVersion"
    nmiconf_trace "Trying to determine version of listener from listener.ora file location"
    set listenerOraFilePrefix [string toupper $listener_home]\\NETWORK\\ADMIN\\
    if { [string first $listenerOraFilePrefix [string toupper $listenerOraFile]] != -1 } {
        if {![string length [nmiconf_getHomeNameFromOracleHome $listener_home]]} {
            nmiconf_trace "OracleHome not found from $listener_home, assuming 7.3.x listener"
            nmiconf_endProcTrace "nmiconf_getListenerOraFileVersion"
            return [nmiconf_Listener73Version]
        } else {
            nmiconf_trace "Listener.ora indicates 8.1.x version from filename"
            nmiconf_endProcTrace "nmiconf_getListenerOraFileVersion"
            return [nmiconf_Listener81Version]
        }
    }
    nmiconf_trace "Matched on listener.ora location to prefix $listenerOraFilePrefix"
    set listenerOraFilePrefix [string toupper $listener_home]\\NET80\\ADMIN\\
    if { [string first $listenerOraFilePrefix [string toupper $listenerOraFile]] != -1 } {
        nmiconf_trace "Located NET80 directory in filespec, version is 8.0.x"
        nmiconf_endProcTrace "nmiconf_getListenerOraFileVersion"
        return [nmiconf_Listener80Version]
    } else {
        nmiconf_trace "A problem was encountered. A home was not matched and it is not 8.0"
    }
    nmiconf_endProcTrace "nmiconf_getListenerOraFileVersion"
}

# Returns the index of the entry in listenerNTServiceList if the entry matches
# the specified listenerName and version
proc nmiconf_findListenerNTService { listenerName version listenerNTServiceList } {
    set serviceIndex 0
    nmiconf_beginProcTrace "nmiconf_findListenerNTService"
    nmiconf_trace "Looking for a listener of name \"$listenerName\"/version \"$version\" in NT ServiceList"
    foreach listenerNTService $listenerNTServiceList {
        nmiconf_trace " - examining against $listenerNTService"
        if { [catch {set listenerNTServiceDetails [nmiconf_getListenerNTServiceDetails $listenerNTService]} msg]} {
            nmiconf_warning $msg
            nmiconf_trace "Error encountered, assuming no listener of name found"
            nmiconf_endProcTrace "nmiconf_findListenerNTService"
            return -1
        }
        set isNameEqual [string compare [string toupper $listenerName] [string toupper [lindex $listenerNTServiceDetails 0]]]
        set isVersionEqual [string compare $version [lindex $listenerNTServiceDetails 1]]
        if { $isNameEqual ==0 && $isVersionEqual == 0 } {
            nmiconf_endProcTrace "nmiconf_findListenerNTService"
            return $serviceIndex
        }
        incr serviceIndex
    }
    nmiconf_trace "List of services exhausted, no matching NT service for the listener"
    nmiconf_endProcTrace "nmiconf_findListenerNTService"
    return -1
}

# for services.ora addresses, which get passed to the console, we'd
# like to eliminate IPC Addresses from any address_list, since the
# they *might* be a valid address for another service on the console's
# own node.  (This is especially likely on NT, since most databases
# have the same SID(?)
proc nmiconf_makeRemoteAddress {address} {
  # whitespace
  set ws {[ ]*}
  # any one-paren-deep NV binding (we're in trouble if there are
  # extra levels of parentheses around)
  set paren {\([^\(\)]*\)}
  # the "(protocol = ipc)" NV
  set ipc "\\(${ws}PROTOCOL${ws}=${ws}IPC${ws}\\)"
  # so this should match any "(address = <whatever>(protocol=ipc)<whatever.)"
    set expression "\\(${ws}ADDRESS${ws}=${ws}(${paren}${ws})*${ipc}${ws}(${paren}${ws})*\\)"
  regsub -all -nocase $expression $address "" new_address

  return $new_address
}

proc nmiconf_getNameFromService {service} {
    # this is kind of a cheat -- since Tcl lists are just strings, and so element == {element},
    # we can just return the first list-element of service.  if service is a string, that's all
    # of it, but if it really is a list, that's the first element, which should be the name.
    return [lindex $service 0]
}

# Generate the output configuration files snmp_ro.ora, snmp_rw.ora and services.ora
#
# services.ora is generated in <agent's oracle home>\\network\\agent directory
#   It contains info about the discovered services which will be sent by the agent
#       when it is discovered from console.
#
# snmp_ro.ora is a readonly file generated in sqlnet config file location like any other sqlnet application.
#   It contains the info about the discovered services which will be used by the agent
#       when interacting with the services during event monitoring and job execution
#
# snmp_rw.ora is a read-write file generated in sqlnet config file location like any other sqlnet application.
#   It contains the index to list of discovered services in snmp_rw.ora and other
#   user configurable parameters for agent.
proc nmiconf_outputConfigFiles { } {

    global ServiceNames ServiceType LocalAddress Parameters ProgramName
    global SID OracleHome Listener
    global ListenerNameInConfigFile ListenerConfigFile ListenerShortName ListenerNTServiceName
    global OutSnmpOraLocation

    nmiconf_beginProcTrace "nmiconf_outputConfigFiles"
    set agentConfigFileLoc [nmiconf_agentConfigFileLoc]

    if {[nmiconf_isFailSafeMode]} {
        nmiconf_trace "In failsafe mode, storing services.ora in $agentConfigFileLoc\\services.ora"
        set ServicesOra [open $agentConfigFileLoc\\services.ora w+]
    } else {
        nmiconf_trace "Storing services.ora in [nmiconf_agentOracleHome]\\network\\agent\\services.ora"
        set ServicesOra [open [nmiconf_agentOracleHome]\\network\\agent\\services.ora w+]
    }

    foreach service $ServiceNames {
        set type $ServiceType($service)
        nmiconf_trace "Writting out services discovered of type: $type"
        set entry "[nmiconf_getNameFromService $service] = \("
        foreach parameter $Parameters($type) {
            global $parameter
            append entry [set [format "%s(%s)" $parameter $service]]
            append entry ", "
        }
        set entry [string range $entry  0 [expr [string length $entry] - 3]]
        append entry "\)\n\n"
        puts $ServicesOra $entry
    }
    close $ServicesOra
    nmiconf_trace "Services.ora writing finished"

    set SnmpFixedName "$agentConfigFileLoc\\snmp_ro.ora"
    set SnmpChangeableName "$agentConfigFileLoc\\snmp_rw.ora"
    set alreadyDiscovered ""
    set index_maxexist 0
    if {[file exists $SnmpChangeableName]} {
      set SnmpChangeable [open $SnmpChangeableName r]
      while {[gets $SnmpChangeable line] >= 0} {
            if {[regexp "^#" $line] || ![regexp -nocase "snmp\.index\." $line]} {
                continue
            }
            set svcname_start [expr [string first "index\." $line] + 6]
            set svcname_end [expr [string first "=" $line] - 1]
            set svcindex_start [expr [string first "=" $line] + 1]
            set svcname [string trim [string range $line $svcname_start $svcname_end] " "]
            set svcindex [string trim [string range $line $svcindex_start end ] " "]
            lappend alreadyDiscovered $svcname
            if { $svcindex > $index_maxexist } {
                set index_maxexist $svcindex
            }
      }
      close $SnmpChangeable
    }

    set visibleServices "snmp.visibleservices = \("
    set fixedParams ""
    set changeableParams ""
    set index [expr $index_maxexist + 1]
    foreach service $ServiceNames {
        # Fix bug#751946, Include OPS instances in the visible
        # services list in the snmp_ro.ora file.
        if {($ServiceType($service) == "ORACLE_DATABASE") || ($ServiceType($service) == "OPS_INSTANCE") || ($ServiceType($service) == "OPS_DATABASE")} {
            append visibleServices "[nmiconf_getNameFromService $service], "
            append fixedParams "snmp.SID.[nmiconf_getNameFromService $service] = $SID($service)\n"
            append fixedParams "snmp.oraclehome.[nmiconf_getNameFromService $service] = $OracleHome($service)\n"
            if {[nmiconf_isTNSAdminDefined]} {
                append fixedParams "snmp.address.[nmiconf_getNameFromService $service] = (ADDRESS=(PROTOCOL=BEQ)(PROGRAM=$ProgramName($service))(ARGV0=$ProgramName($service)$SID($service))(ARGS='(DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))'))\n"
            } elseif {[nmiconf_isTNSAdminDefined $OracleHome($service)]} {
                append fixedParams "snmp.address.[nmiconf_getNameFromService $service] = (ADDRESS=(PROTOCOL=BEQ)(PROGRAM=$ProgramName($service))(ARGV0=$ProgramName($service)$SID($service))(ARGS='(DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))'))\n"
            } else {
                append fixedParams "snmp.address.[nmiconf_getNameFromService $service] = (ADDRESS=(PROTOCOL=BEQ)(PROGRAM=$ProgramName($service))(ARGV0=$ProgramName($service)$SID($service))(ARGS='(DESCRIPTION=(LOCAL=YES)(ADDRESS=(PROTOCOL=beq)))'))\n"
            }
            append fixedParams "snmp.remoteaddress.[nmiconf_getNameFromService $service] = $ServiceAddress($service)\n"
            if {([lsearch $alreadyDiscovered [nmiconf_getNameFromService $service]] == -1)} {
                append changeableParams "snmp.contact.[nmiconf_getNameFromService $service] = \"\"\n"
                append changeableParams "snmp.index.[nmiconf_getNameFromService $service] = $index\n"
            }
            incr index
        } elseif {$ServiceType($service) == "ORACLE_LISTENER"} {
            append visibleServices "$ListenerShortName($service), "
            append fixedParams "snmp.shortname.$ListenerShortName($service) = $ListenerNameInConfigFile($service)\n"
            append fixedParams "snmp.longname.$ListenerShortName($service) = [nmiconf_getNameFromService $service]\n"
            append fixedParams "snmp.configfile.$ListenerShortName($service) = $ListenerConfigFile($service)\n"
            append fixedParams "snmp.oraclehome.$ListenerShortName($service) = $OracleHome($service)\n"
            append fixedParams "snmp.servicename.[nmiconf_getNameFromService $service] = $ListenerNTServiceName($service)\n"

            if {([lsearch $alreadyDiscovered $ListenerShortName($service)] == -1)} {
                append changeableParams "snmp.contact.$ListenerShortName($service) = \"\"\n"
                append changeableParams "snmp.index.$ListenerShortName($service) = $index\n"
            }
            incr index
        }
    }

    if {([string first "(" $visibleServices] + 1) < [string length $visibleServices]} {
        set visibleServices [string range $visibleServices 0 [expr [string length $visibleServices] - 3]]
    }
    append visibleServices "\)"
    set SnmpFixed [open $SnmpFixedName w]
    puts $SnmpFixed $visibleServices
    puts -nonewline $SnmpFixed $fixedParams
    puts $SnmpFixed "ifile = $SnmpChangeableName"
    if {[file exists $agentConfigFileLoc\\sqlnet.ora]} {
        puts $SnmpFixed "ifile = $agentConfigFileLoc\\sqlnet.ora"
    }
    close $SnmpFixed
    nmiconf_trace "Updated snmp_ro.ora"

    #appends a newline character to the end of file if one does not already exist
    #This extra check was introduced to fix bug#729929

   if {[file exists $SnmpChangeableName]} {
        set SnmpChangeable [open $SnmpChangeableName a+]
        if { [catch {seek $SnmpChangeable -1 end} msg] } {
            nmiconf_log  "Error : error in seek operation of $SnmpChangeableName file : $msg"
        }
        set lastchar [read $SnmpChangeable 1]
        set newline "\n"
        if { $lastchar == $newline } {
            puts -nonewline $SnmpChangeable $changeableParams
        } else {
            puts -nonewline $SnmpChangeable $newline
            puts -nonewline $SnmpChangeable $changeableParams
        }
    } else {
        set SnmpChangeable [open $SnmpChangeableName a]
        puts -nonewline $SnmpChangeable $changeableParams
    }
    close $SnmpChangeable
    nmiconf_trace "Updated snmp_rw.ora"
    nmiconf_endProcTrace "nmiconf_outputConfigFiles"
}

# Return the NT service name of the agent
proc nmiconf_agentNTServiceName {} {
    global NT_ServiceName
    # The agent sets NT_ServiceName variable to NT service name of the agent
    # before nmiconf.tcl is sourced.
    if {[info exists NT_ServiceName]} {
        return $NT_ServiceName
    }

    return ""
}

# Extract ORACLE_HOME of agent using the agent's NT service name in registry
# Note that we can't rely on ORACLE_HOME env variable for multiple
# oracle home compliant agent. But give preference to env if it is defined.
proc nmiconf_agentOracleHome {} {
    global nmiconf_AgentOracleHome
    global env

    if {[info exists nmiconf_AgentOracleHome]} {
        return $nmiconf_AgentOracleHome
    }

    nmiconf_beginProcTrace "nmiconf_agentOracleHome"
    if {[info exists env(ORACLE_HOME)]} {
        set nmiconf_AgentOracleHome $env(ORACLE_HOME)
        nmiconf_trace "Agent ORACLE_HOME set to $nmiconf_AgentOracleHome via ORACLE_HOME env symbol"
    }   elseif {[catch {regfind HKEY_LOCAL_MACHINE System\\CurrentControlSet\\Services\\[nmiconf_agentNTServiceName] ImagePath} out] == 0} {

        set OhomeIndex [string last "\\" [string range $out 0 [expr [string last "\\" $out] - 1 ]]]
        set nmiconf_AgentOracleHome [string range $out 0 [expr $OhomeIndex -1]]
        nmiconf_trace "Agent ORACLE_HOME retrieved from registry key"
        nmiconf_trace "   HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services\\[nmiconf_agentNTServiceName].ImagePath = $out"
        nmiconf_trace "   Agent Home == $nmiconf_AgentOracleHome"
    } else {
        nmiconf_endProcTrace "nmiconf_agentOracleHome"
        error "Could not determine Agent's ORACLE_HOME"
    }

    nmiconf_endProcTrace "nmiconf_agentOracleHome"
    return $nmiconf_AgentOracleHome
}

# Determine whether the agent is running in failsafe configuration
proc nmiconf_isFailSafeMode {} {
    global nmiconf_FailSafeMode

    if {[info exists nmiconf_FailSafeMode]} {
        return $nmiconf_FailSafeMode
    }

    nmiconf_beginProcTrace "nmiconf_isFailSafeMode"
    if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName] Network_Name} ] == 0} {
        nmiconf_trace "Failsafe config detected!"
        nmiconf_trace "Failsafe registry key: HKEY_LOCAL_MACHINE\\Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName].Network_Name"
        set nmiconf_FailSafeMode 1
    } else {
        set nmiconf_FailSafeMode 0
    }
    nmiconf_endProcTrace "nmiconf_isFailSafeMode"
    return $nmiconf_FailSafeMode
}

# Returns TNS_HOME as value of home location for a home which has TNS_ADMIN defined.
proc nmiconf_getTNSHome {} {
    return TNS_HOME
}

# Returns 1 if the specified home value is TNS_HOME
proc nmiconf_isTNSHome {home} {
    set isTNSHome [string compare [string toupper $home] TNS_HOME]
    if { $isTNSHome }   {
        return 0
    }

    return 1
}

# Determine whether the TNS_ADMIN variable is defined for the specified home in registry
# If the optional parameter home is not specified, check the TNS_ADMIN environment variable.
proc nmiconf_isTNSAdminDefined { {home ""} } {

    global env
    nmiconf_beginProcTrace "nmiconf_isTNSAdminDefined"
    if {![llength $home] } {
        nmiconf_trace "Looking for TNS_ADMIN for the system, not in a specific home"
        if {![info exists env(TNS_ADMIN)]} {
            nmiconf_trace "TNS_ADMIN is not defined"
            nmiconf_endProcTrace "nmiconf_isTNSAdminDefined"
            return 0
        }
        set tnsAdminLoc $env(TNS_ADMIN)
        if {[file exists $tnsAdminLoc] && [file isdirectory $tnsAdminLoc]} {
            nmiconf_trace "TNS_ADMIN defined and the directory exists"
            nmiconf_endProcTrace "nmiconf_isTNSAdminDefined"
            return 1
        } else {
            nmiconf_trace "TNS_ADMIN is defined but the directory does not exist - assuming no TNS_ADMIN"
            nmiconf_endProcTrace "nmiconf_isTNSAdminDefined"
            return 0
        }
    }

    if { [catch {set tnsAdminLoc [nmiconf_TNSAdminLoc $home]} msg] } {
        nmiconf_trace "Error encountered in getting TNS_ADMIN, assumed not defined"
        nmiconf_endProcTrace "nmiconf_isTNSAdminDefined"
        return 0
    }

    if {[string length $tnsAdminLoc] > 0 } {
        nmiconf_trace "TNS_ADMIN is defined"
        nmiconf_endProcTrace "nmiconf_isTNSAdminDefined"
        return 1
    }

    nmiconf_trace "TNS_ADMIN is not defined"
    nmiconf_endProcTrace "nmiconf_isTNSAdminDefined"
    return 0
}

# Get the value of the TNS_ADMIN variable of the specified home
# The TNS_ADMIN is defined as registry key under the oracle home.
# If optional parameter, home is not specified, get value from environment.
# Note: Strips the trailing path separator. i.e C:\\ will be returned as C:
proc nmiconf_TNSAdminLoc { {home ""} } {

    global env
    set tnsAdminLoc ""
    nmiconf_beginProcTrace "nmiconf_TNSAdminLoc"
    if {![llength $home]} {
        nmiconf_trace "Getting location of TNS_ADMIN"
    } else {
        nmiconf_trace "Getting location of TNS_ADMIN for oracleHome $home"
    }

    if {![llength $home] && [info exists env(TNS_ADMIN)]} {
        set tnsAdminLoc $env(TNS_ADMIN)
        if {![file exists $tnsAdminLoc]} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "Invalid TNS_ADMIN location $TNSAdminLoc"
        }
        if {![file isdirectory $tnsAdminLoc]} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "TNS_ADMIN location $TNSAdminLoc is not a directory"
        }

        set tnsAdminLoc [string trim $tnsAdminLoc]
        set tnsAdminLoc [string trimright $tnsAdminLoc "\\"]
        nmiconf_trace "TNS_ADMIN set via env variable to \"$tnsAdminLoc\""
        nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
        return $tnsAdminLoc
    }

    nmiconf_trace "Looking for TNS_ADMIN via oracle.key"
    set oracleHomeKeyFile $home\\bin\\Oracle.key
    if {![file exists $oracleHomeKeyFile]} {
        nmiconf_trace "oracle.key is not found as oracleHomeKeyFile, assuming older home"
        if {[catch {regfind HKEY_LOCAL_MACHINE SOFTWARE\\ORACLE ORACLE_HOME} chkhome] == 0} {
            if {$home == $chkhome} {
                nmiconf_trace "Using oracleHomeKey for older home of HKEY_LOCAL_MACHINE\\SOFTWARE\\ORACLE.ORACLE_HOME"
                set oracleHomeKey SOFTWARE\\ORACLE
            } else {
                nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
                error "Could not determine TNS_ADMIN value : $oracleHomeKeyFile doesn't exist"
            }
        } else {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "Could not read ORACLE_HOME key from DEFAULT_HOME registry"
        }
    } else {
        if {[catch {set OracleKey [open $oracleHomeKeyFile r]}]} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "Could not determine TNS_ADMIN value : Not able to open $oracleHomeKeyFile "
        }

        if {[catch {gets $OracleKey oracleHomeKey}]} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "Could not determine TNS_ADMIN value : Not able to read $oracleHomeKeyFile "
        }

        close $OracleKey

        set oracleHomeKey [string trim $oracleHomeKey]
        nmiconf_trace "oracle.key refers to oracleHomeKey of $oracleHomeKey"
    }
    if {[catch {regfind HKEY_LOCAL_MACHINE $oracleHomeKey ORACLE_HOME} chkhome] == 0} {
        if {$home != $chkhome} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "ORACLE.KEY file contains bad registry hive: ORACLE_HOME $home does not match $chkhome"
        }
    } else {
        nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
        error "Could not read ORACLE_HOME key from DEFAULT_HOME registry"
    }

    if {[catch {regfind HKEY_LOCAL_MACHINE $oracleHomeKey TNS_ADMIN} out] == 0} {
        set tnsAdminLoc $out
        if {![file exists $tnsAdminLoc]} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "Invalid TNS_ADMIN location $tnsAdminLoc"
        }
        if {![file isdirectory $tnsAdminLoc]} {
            nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
            error "TNS_ADMIN location $tnsAdminLoc is not a directory"
        }
    } else {
        nmiconf_trace "No TNS_ADMIN value found for HKEY_LOCAL_MACHINE\\$oracleHomeKey"
    }

    set tnsAdminLoc [string trim $tnsAdminLoc]
    set tnsAdminLoc [string trimright $tnsAdminLoc "\\"]
    nmiconf_trace "TNS_ADMIN found in registry for home $home with value: \"$tnsAdminLoc\""
    nmiconf_endProcTrace "nmiconf_TNSAdminLoc"
    return $tnsAdminLoc
}

# Determine the file location where the sqlnet configuration files are stored
# The sqlnet configuration files are listener.ora, sqlnet.ora and tnsnames.ora
proc nmiconf_agentSqlnetConfigLoc {} {
    global nmiconf_AgentSqlnetConfigLoc

    if {[info exists nmiconf_AgentSqlnetConfigLoc]} {
        return $nmiconf_AgentSqlnetConfigLoc
    }

    set agentHome [nmiconf_agentOracleHome]

    # Check if TNS_ADMIN is defined as environment variable
    # else check if it is defined specific to agent's home.
    if {[nmiconf_isTNSAdminDefined]} {
        set nmiconf_AgentSqlnetConfigLoc [nmiconf_TNSAdminLoc]
    } elseif {[nmiconf_isTNSAdminDefined $agentHome]} {
        set nmiconf_AgentSqlnetConfigLoc [nmiconf_TNSAdminLoc $agentHome]
    } else {
        set nmiconf_AgentSqlnetConfigLoc $agentHome\\network\\admin
    }

    return $nmiconf_AgentSqlnetConfigLoc
}

# Determine the file location where the agent configuration files will be stored
# Typically the agent stores its config files in sqlnet config location except during
# fail safe mode. The agent config file location is specified in registry for fail safe mode.
# The agent configuration files are snmp_rw.ora and snmp_ro.ora
proc nmiconf_agentConfigFileLoc {} {
    global nmiconf_AgentConfigFileLoc

    if {[info exists nmiconf_AgentConfigFileLoc]} {
        return $nmiconf_AgentConfigFileLoc
    }

    nmiconf_beginProcTrace "nmiconf_agentConfigFileLoc"
    if {[nmiconf_isFailSafeMode]} {
        if {[catch {regfind HKEY_LOCAL_MACHINE  Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName] ConfigPath} out] != 0} {
            nmiconf_endProcTrace "nmiconf_agentConfigFileLoc"
            error "Could not determine agent Config file location in fail safe mode"
        }
        set nmiconf_AgentConfigFileLoc $out
        nmiconf_trace "in Failsafe mode, config location is $out"

        if {![file exists $nmiconf_AgentConfigFileLoc]} {
            if {[catch {exec md $nmiconf_AgentConfigFileLoc} out] != 0} {
                nmiconf_endProcTrace "nmiconf_agentConfigFileLoc"
                error "Unable to create Config File location in fail safe mode"
            }
        }

        nmiconf_endProcTrace "nmiconf_agentConfigFileLoc"
        return $nmiconf_AgentConfigFileLoc
    }

    set nmiconf_AgentConfigFileLoc [nmiconf_agentSqlnetConfigLoc]
    nmiconf_trace "agent config location is: $nmiconf_AgentConfigFileLoc"
    nmiconf_endProcTrace "nmiconf_agentConfigFileLoc"
    return $nmiconf_AgentConfigFileLoc
}

# Returns the default domain value specified in sqlnet.ora file of agent's oracle home
proc nmiconf_agentDefaultDomain {} {
    global nmiconf_AgentDefaultDomain
    if {[info exists nmiconf_AgentDefaultDomain]} {
        return $nmiconf_AgentDefaultDomain
    }

    set sqlnetOraFile [nmiconf_agentSqlnetConfigLoc]\\sqlnet.ora
    if {![file exists $sqlnetOraFile]} {
        set nmiconf_AgentDefaultDomain ""
        return $nmiconf_AgentDefaultDomain
    }

    set sqlnetOraFileHdl [open $sqlnetOraFile r]
    while {[nmiconf_getOraLine $sqlnetOraFileHdl key value] >= 0} {
        #This is to prevent the NT backlash problem from producing a zero length
        #services.ora, it is unlikely that there will be a value in sqlnet.ora
        #which will have "\" in it.
        regsub -all {\\} $value {/} value
        if {[regexp -nocase "names.default_domain" $key]} {
             set nmiconf_AgentDefaultDomain .$value
             break
        }
    }
    close $sqlnetOraFileHdl

    # names.default_domain parameter might not be configured in sqlnet.ora
    if {![info exists nmiconf_AgentDefaultDomain]} {
        set nmiconf_AgentDefaultDomain ""
    }

    return $nmiconf_AgentDefaultDomain
}

# Preparation of host list.
# The host list will contain the host name, host aliases and ip
# addresses of the machine.
# The list is needed to locate entries specific to this machine from
# tnsnames.ora and listener.ora files
proc nmiconf_getHosts { } {

    global argv
    set hostList {}
    # In fail safe mode the host list is generated by looking in the registry
    if {[nmiconf_isFailSafeMode] } {
        if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName] Network_Name} hostList] != 0} {
            error "Couldnot retrieve network name in fail safe mode"
        }
        if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName] IPAddress} IPAddressResult] != 0} {
            error "Couldnot retrieve ip address in fail safe mode"
        }
        lappend hostList $IPAddressResult
    } else {
    # In normal mode the host list is supplied by argv variable set by agent
    # before nmiconf.tcl is sourced.
        set hostList $argv
    }

    return $hostList
}

# Returns the host name of the agent.
proc nmiconf_agentHostName {} {

    global nmiconf_AgentHostName

    if {[info exists nmiconf_AgentHostName ]} {
        return $nmiconf_AgentHostName
    }

    # The first entry of list returned by nmiconf_getHosts is the agent's host name in failsafe mode
        # otherwise it's the third entry
    if {[nmiconf_isFailSafeMode] } {
        set nmiconf_AgentHostName [lindex [nmiconf_getHosts] 0]
    } else {
    # 2nd nodename in arg list is official hostname (h_name)
        set nmiconf_AgentHostName [lindex [nmiconf_getHosts] 1]
    }
    return $nmiconf_AgentHostName
}

# Returns the official host name of the agent.
proc nmiconf_officialHostName {} {
    # nmiconf_agentHostName already does the right thing on NT, so just use it
  # In non-failsafe mode, second host list entry returned by
  # nmiconf_agentHost is the official hostname (assumed to be the fully
  # qualified/canonical name), so use that so it's consistent with name
  # sent by agent in notifications to client/OMS - for bug 1157884
  # Should be fully qualified if using a name server.  If using a hosts file,
  # official hostname is the first entry after the address column (so this
  # entry must be, or changed to be, fully qualified in order for the agent
  # to use fully qualified hostname).
  # In failsafe mode nmiconf_agentHostName returns hostname from registry.

    return [nmiconf_agentHostName]
}

# Returns 1 if the specified sqlnet address is configured for this machine.
proc nmiconf_isThisHostAddress { address } {
    set hostList [nmiconf_getHosts]
    foreach hostname $hostList {
        if {[regexp -nocase "\\(\[ \]*host\[ \]*=\[ \]*$hostname\[ \]*\\)" $address]} {
            return 1
        }
    }
    return 0
}

# Find all the oracle homes installed on this machine by inspecting the registry.
# Multiple oracle home (MOH) compliant installer create homes under
# HKEY_LOCAL_MACHINE Software\\oracle as Home0, Home1 ...
#
# Returns the list of the form { oracleHome1 oracleHome2 ... oracleHomeN }
proc nmiconf_getAllOracleHomes { } {
    global nmiconf_OracleHomeList

    if {[info exists nmiconf_OracleHomeList]} {
        return $nmiconf_OracleHomeList
    }

    set nmiconf_OracleHomeList {}
    if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle LIST} oracleKeyList] != 0} {
        error "Could not retrieve registry subkeys under HKEY_LOCAL_MACHINE\\Software\\Oracle"
    }

    foreach oracleKey $oracleKeyList {
        if {[string match "HOME*" $oracleKey] == 1} {
            if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle\\$oracleKey ORACLE_HOME} oracleHome] == 0} {
                set oracleHomeEntry {}
                set oracleHome [string trimright [string trim $oracleHome] "\\"]
                set homeNameResult [catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle\\$oracleKey ORACLE_HOME_NAME} oracleHomeName]
                if {$homeNameResult != 0} {
                    # Its ok for HOME0 not to have ORACLE_HOME_NAME set
                    nmiconf_warning "Could not retrieve registry value ORACLE_HOME_NAME under HKEY_LOCAL_MACHINE\\Software\\Oracle\\$oracleKey"
                    set oracleHomeName ""
                }
                lappend oracleHomeEntry $oracleHome
                lappend oracleHomeEntry [string trim $oracleHomeName]
                lappend nmiconf_OracleHomeList $oracleHomeEntry
            } else {
                if { [string compare [string toupper $oracleKey] HOME0] } {
                    # Its ok for HOME0 not to have ORACLE_HOME set
                    nmiconf_warning "Could not retrieve registry value ORACLE_HOME under HKEY_LOCAL_MACHINE\\Software\\Oracle\\$oracleKey"
                }
            }
        }
    }

    # look for default Home.
    if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle ORACLE_HOME} defaultHome] == 0} {
        set defaultHome [string trimright [string trim $defaultHome] "\\"]
        set defaultHomeNameResult [catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle ORACLE_HOME_NAME} defaultHomeName]
        if {$defaultHomeNameResult != 0} {
            set defaultHomeName ""
        }

        set defaultHomeEntry {}
        lappend defaultHomeEntry $defaultHome
        lappend defaultHomeEntry [string trim $defaultHomeName]
        if {[lsearch -exact $nmiconf_OracleHomeList $defaultHomeEntry ] == -1} {
            set nmiconf_OracleHomeList [linsert $nmiconf_OracleHomeList 0 $defaultHomeEntry]
        }
    }

    return $nmiconf_OracleHomeList
}

# Find the list of oracle homes which contain databases and the
# associated database SIDs
#
# Returns list of the form { {SID1 oracleHome1 NTServiceName1 ProgramName1} {SID2 oracleHome2 NTServiceName2 ProgramName2} ... {SIDN oracleHomeN NTServiceNameN ProgramNameN} }
proc nmiconf_getDatabaseEntries { } {
    set databaseEntries {}
    nmiconf_beginProcTrace "nmiconf_getDatabaseEntries"
    if {[nmiconf_isFailSafeMode]} {
        if {[catch {regfind HKEY_LOCAL_MACHINE Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName] SID_LIST} sidlist] != 0} {
            nmiconf_endProcTrace "nmiconf_getDatabaseEntries"
            error "Could not retrieve database SID list in fail safe mode"
        }
        nmiconf_trace "Failsafe SID list for this agent (HKEY_LOCAL_MACHINE\\Software\\Oracle\\FailSafe\\Agent\\[nmiconf_agentNTServiceName].SID_LIST) == $sidlist"
        foreach rsid $sidlist {
            set databaseEntry {}
            # In Failsafe mode the failover databases have to be in the agent's home.
            lappend databaseEntry $rsid
            lappend databaseEntry [nmiconf_agentOracleHome]
            lappend databaseEntry OracleService$rsid
            #find the program name(executable) for the Bequeath connect descriptor
            if {[catch {regfind HKEY_LOCAL_MACHINE System\\CurrentControlSet\\Services\\OracleService$rsid ImagePath} out] != 0} {
                nmiconf_endProcTrace "nmiconf_getDatabaseEntries"
                error "Could not read ImagePath of Database: $rsid"
            }
            #find index which will help get executable name
            set ProgIndex [expr [string last "\\" $out] + 1]
            set ProgEndIndex [expr [string last "." $out] - 1]
            set ProgName [string range $out $ProgIndex $ProgEndIndex]
            lappend databaseEntry $ProgName
            lappend databaseEntries $databaseEntry
            nmiconf_trace "Adding Failsafe entry for SID $rsid"
            nmiconf_trace "  - sid: $rsid"
            nmiconf_trace "  - OracleHome: [nmiconf_agentOracleHome]"
            nmiconf_trace "  - service: OracleService$rsid"
            nmiconf_trace "  - progName: $ProgName"
        }
        nmiconf_endProcTrace "nmiconf_getDatabaseEntries"
        return $databaseEntries
    }

    nmiconf_trace "Walking list of services in HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\Services for SIDs"
    if {[catch {regfind HKEY_LOCAL_MACHINE System\\CurrentControlSet\\Services LIST} NTServiceKeyList] != 0} {
        nmiconf_endProcTrace "nmiconf_getDatabaseEntries"
        error "Could not read NT service names"
    }
    foreach NTServiceKey $NTServiceKeyList {
        set databaseEntry {}
        if {[string match "OracleService*" $NTServiceKey]} {
            nmiconf_trace "$NTServiceKey is for a database instance - matched OracleService*"
            if {[catch {regfind HKEY_LOCAL_MACHINE SYSTEM\\CurrentControlSet\\Services\\$NTServiceKey ImagePath} out] == 0} {
                #find the ORACLE_HOME based on the string in "out"
                set OhomeIndex [string last "\\" [string range $out 0 [expr [string last "\\" $out] - 1 ]]]
                set databaseHome [string range $out 0 [expr $OhomeIndex -1]]
                set databaseSID [string range $NTServiceKey 13 end]
                #find index which will help get executable name
                set ProgIndex [expr [string last "\\" $out] + 1]
                set ProgEndIndex [expr [string last "." $out] - 1]
                set ProgName [string range $out $ProgIndex $ProgEndIndex]
                lappend databaseEntry $databaseSID
                lappend databaseEntry $databaseHome
                lappend databaseEntry $NTServiceKey
                lappend databaseEntry $ProgName
                lappend databaseEntries $databaseEntry
                nmiconf_trace "Adding instance entry for SID $databaseSID"
                nmiconf_trace "  - sid: $databaseSID"
                nmiconf_trace "  - OracleHome: $databaseHome"
                nmiconf_trace "  - service: $NTServiceKey"
                nmiconf_trace "  - progName: $ProgName"
            } else {
                nmiconf_trace "$NTServiceKey discounted as no \"ImagePath\" value found"
            }
        }
    }

    nmiconf_endProcTrace "nmiconf_getDatabaseEntries"
    return $databaseEntries
}

# Collects the listener nt service names in the following form
# {
#   {home1 home2 ... homeN}
#   {home1NTService1 home1NTService2 ... home1NTServiceN}
#       {home2NTService1 home2NTService2 ... home2NTServiceN}
#       ...
#       {homeMNTService1 homeMNTService2 ... homeMNTServiceN}
# }
#
# Note that the first entry is a index of homes.
proc nmiconf_getListenerNTServiceEntries {} {
    # Find the oracle homes which contain listeners and the associated
    # NT service name of the listeners
    if {[catch {regfind HKEY_LOCAL_MACHINE System\\CurrentControlSet\\Services LIST} NTServiceKeyList] != 0} {
        error "Could not read NT service names"
    }
    nmiconf_beginProcTrace "nmiconf_getListenerNTServiceEntries"
    nmiconf_trace "Finding listeners and their and their associated oracleHomes"

    set listenerNTServiceEntryList {}
    set listenerHomeList {}
    nmiconf_trace "Looping thru the list of services looking for a match of Oracle*TNSListener*"
    foreach NTServiceKey $NTServiceKeyList {
        if [string match "Oracle*TNSListener*" $NTServiceKey] {
            if {[catch {regfind HKEY_LOCAL_MACHINE SYSTEM\\CurrentControlSet\\Services\\$NTServiceKey ImagePath} out] == 0} {
                nmiconf_trace "Found listener, service = $NTServiceKey"
                set bin_no [string first BIN $out]
                set bin_no [expr ($bin_no - 2)]
                set listenerHome [string range $out 0 $bin_no]
                nmiconf_trace "Stripped out first \"bin\" from $out to get listenerHome of $listenerHome"
                set homePos [lsearch -exact [string toupper $listenerHomeList] [string toupper $listenerHome]];
                if { $homePos == -1} {
                    nmiconf_trace "$listenerHome not previously found in list of OracleHomes containing listeners"
                    nmiconf_trace "Adding $NTServiceKey as a listener in $listenerHome"
                    lappend listenerHomeList $listenerHome
                    set serviceList {}
                    lappend serviceList $NTServiceKey
                    if {![llength $listenerNTServiceEntryList]} {
                        lappend listenerNTServiceEntryList $listenerHomeList
                        lappend listenerNTServiceEntryList $serviceList
                    } else {
                        set listenerNTServiceEntryList [lreplace $listenerNTServiceEntryList 0 0 $listenerHomeList]
                        lappend listenerNTServiceEntryList $serviceList
                    }
                } else {
                    nmiconf_trace "$listenerHome found in list of OracleHomes containing listeners"
                    nmiconf_trace "Adding $NTServiceKey as a listener in $listenerHome"
                    set serviceListPos [expr {$homePos + 1}]
                    set serviceList [lindex $listenerNTServiceEntryList $serviceListPos]
                    lappend serviceList $NTServiceKey
                    set listenerNTServiceEntryList [lreplace $listenerNTServiceEntryList $serviceListPos $serviceListPos $serviceList]
                }
            } else {
                nmiconf_trace "Service $NTServiceKey is invalid - missing ImagePath value, discarding"
            }
        }
    }

    nmiconf_endProcTrace "nmiconf_getListenerNTServiceEntries"
    return $listenerNTServiceEntryList
}

# Collects the listener.ora files in the following form
# {
#   {home1, home2 ... homeN}
#       {
#           {home1\\NETWORK\ADMIN\\listener.ora home1\\NET80\ADMIN\\listener.ora}
#           {home1NTService1 home1NTService2 ... home1NTServiceN}
#       }
#       {
#           {home2\\NET80\ADMIN\\listener.ora}
#           {home2NTService1 home2NTService2 ... home2NTServiceN}
#       }
#       ...
#       {
#           {homeM\\NETWORK\ADMIN\\listener.ora}
#           {homeMNTService1 homeMNTService2 ... homeMNTServiceN}
#       }
# }
#
# Note that the first entry is a index of homes.
# A home will have 2 listener.ora files if it has Net80 as well as Pre-Net80 versions installed
# In the above example home1 has both Net80 as well as Pre-Net80 installed whereas
# home2 has only Net80 installed and homeN has only Pre-Net80 installed.
#
# If TNS_ADMIN environment variable is defined then the list will of the form
# {
#   {TNS_HOME}
#   {{$path\\listener.ora} { listener nt services of all the homes }}
#   }
#
#   If a particular home as a TNS_ADMIN variable defined as registry variable then
# the corresponding home value in home index will be TNS_HOME.
# i.e. {home1, TNS_HOME, home3 ... homeN} -- here home2 has TNS_ADMIN set in registry
proc nmiconf_collectListenerOraFiles {} {

    # used to store the index of homes
    set listenerOraHomeList {}

    # used to store the list of listener.ora files of current home
    set listenerOraHomeFileList {}

    # used to store the list of listener nt service names of current home
    set listenerOraServiceEntryList {}

    # entry containing listenerOraHomeFileList and listenerOraServiceEntryList
    set listenerOraFileEntryList {}

    # Master list returned by this procedure
    set listenerOraFileList {}

    nmiconf_beginProcTrace "nmiconf_collectListenerOraFiles"
    set listenerNTServiceEntryList [nmiconf_getListenerNTServiceEntries]

    if {[nmiconf_isTNSAdminDefined]} {
        set listenerOraFile [nmiconf_TNSAdminLoc]\\listener.ora
        if {[file exists $listenerOraFile]} {
            nmiconf_trace "Using listener.ora located via TNS_ADMIN env, listener file \"$listenerOraFile\""
            # In the case of TNS_ADMIN env set home to TNS_HOME
            lappend listenerOraHomeList [nmiconf_getTNSHome]
            lappend listenerOraHomeFileList $listenerOraFile

            lappend listenerOraFileEntryList $listenerOraHomeFileList

            # Flatten the service groups (based on home) into a single list
            set restOfServiceList [lrange $listenerNTServiceEntryList 1 end]
            set listenerOraServiceEntryList ""
            foreach serviceEntry $restOfServiceList {
                set listenerOraServiceEntryList [concat $listenerOraServiceEntryList $serviceEntry]
            }

            lappend listenerOraFileEntryList $listenerOraServiceEntryList

            lappend listenerOraFileList $listenerOraHomeList
            lappend listenerOraFileList $listenerOraFileEntryList
            nmiconf_endProcTrace "nmiconf_collectListenerOraFiles"
            return $listenerOraFileList
        } else {
            nmiconf_trace "A listener file defined (via TNS_ADMIN env) does not exist, continuing..."
        }
    }

    set listenerHomeList [lindex $listenerNTServiceEntryList 0]
    set listenerOraFile ""
    nmiconf_trace "Iterating thru the list of homes found where listeners were configured..."
    foreach listenerHome $listenerHomeList {
        nmiconf_trace " - Examining listener home $listenerHome"
        set listenerOraHomeFileList {}
        set listenerOraFileEntryList {}

        unset listenerOraFile
        if {[nmiconf_isTNSAdminDefined $listenerHome]} {
            nmiconf_trace " --  TNS_ADMIN defined for OracleHome $listenerHome"
            set listenerOraFile [nmiconf_TNSAdminLoc $listenerHome]\\listener.ora
        }

        if {[info exists listenerOraFile] && [file exists $listenerOraFile]} {
            lappend listenerOraHomeList [nmiconf_getTNSHome]
            lappend listenerOraHomeFileList $listenerOraFile
            nmiconf_trace " --  Listener file ($listenerOraFile) found"
        } else {
            # Check for Net80 listener
            set listenerOraFile $listenerHome\\net80\\admin\\listener.ora
            nmiconf_trace " --  Looking in net80 directory for a listener config"
            if {[file exists $listenerOraFile]} {
                lappend listenerOraHomeList $listenerHome
                lappend listenerOraHomeFileList $listenerOraFile
                nmiconf_trace " --  Listener file ($listenerOraFile) found"
            }

            # Check for Net30 listener
            set listenerOraFile $listenerHome\\network\\admin\\listener.ora
            nmiconf_trace " --  Finally, looking in network directory for a listener config"
            if {[file exists $listenerOraFile]} {
                lappend listenerOraHomeList $listenerHome
                lappend listenerOraHomeFileList $listenerOraFile
                nmiconf_trace " --  Listener file ($listenerOraFile) found"
            }
        }

        # No listeners (actually listener.ora files) found in current home
        if {![llength $listenerOraHomeFileList]} {
            nmiconf_trace " - No listener.ora file found for home $listenerHome, skipping listeners that were found registered using this home"
            continue
        }

        # Get the listener nt services corresponding to this home
        lappend listenerOraFileEntryList $listenerOraHomeFileList
        set serviceListPos [lsearch -exact [string toupper $listenerHomeList] [string toupper $listenerHome]]
        incr serviceListPos
        lappend listenerOraFileEntryList [lindex $listenerNTServiceEntryList $serviceListPos]
        nmiconf_trace " --  Listeners corresponding to this home: [lindex $listenerNTServiceEntryList $serviceListPos]"

        if {![llength $listenerOraFileList]} {
            # Empty list case
            lappend listenerOraFileList $listenerOraHomeList
            lappend listenerOraFileList $listenerOraFileEntryList
        }   else {
            set listenerOraFileList [lreplace $listenerOraFileList 0 0 $listenerOraHomeList]
            lappend listenerOraFileList $listenerOraFileEntryList
        }
    }

    nmiconf_endProcTrace "nmiconf_collectListenerOraFiles"
    return $listenerOraFileList
}

# Reads all the listener.ora files of all the oracle homes and loads the info of all the listeners
# into the globallook-up tables of listeners
# See nmiconf_collectListenerOraFiles and nmiconf_readListenerOra for more details
proc nmiconf_loadListenerInfo {} {

    # The following globals will be modified by nmiconf_readListenerOra procedure.
    global listener_list listener_samename_index_list

    set listener_list {}
    set listener_samename_index_list {}

    nmiconf_beginProcTrace "nmiconf_loadListenerInfo"
    nmiconf_trace "Loading Listener info from system..."
    set listenerOraFileList [nmiconf_collectListenerOraFiles]
    set listenerOraHomeList [lindex $listenerOraFileList 0]
    set homeIndex 1
    foreach listenerOraHome $listenerOraHomeList {
        set listenerOraFileEntryList [lindex $listenerOraFileList $homeIndex]
        set listenerOraHomeFileList [lindex $listenerOraFileEntryList 0]
        set listenerOraServiceEntryList [lindex $listenerOraFileEntryList 1]
        foreach listenerOraFile $listenerOraHomeFileList {
            if { [catch {set listenerOraServiceEntryList [nmiconf_readListenerOra $listenerOraFile $listenerOraHome $listenerOraServiceEntryList]} msg] } {
                nmiconf_warning "Error while parsing $listenerOraFile : $msg"
            }
        }

        if {[llength $listenerOraServiceEntryList]} {
            foreach serviceEntry $listenerOraServiceEntryList {
                nmiconf_warning "Skipping $serviceEntry : Could not find corresponding listener definition in $listenerOraHomeFileList"
            }
        }
        incr homeIndex
    }
    nmiconf_endProcTrace "nmiconf_loadListenerInfo"
}

# Collects the tnsnames.ora files in the following form
# {
#   {home1, home2 ... homeN}
#       {home1\\NETWORK\ADMIN\\tnsnames.ora home1\\NET80\ADMIN\\tnsnames.ora}
#       {home2\\NET80\ADMIN\\tnsnames.ora}
#       ...
#       {homeM\\NETWORK\ADMIN\\tnsnames.ora}
# }
#
# Note that the first entry is a index of homes.
# A home will have 2 tnsnames.ora files if it has Net80 as well as Pre-Net80 versions installed
# In the above example home1 has both Net80 as well as Pre-Net80 installed whereas
# home2 has only Net80 installed and homeN has only Pre-Net80 installed.
#
# If TNS_ADMIN environment variable is defined then the list will be of the form
# {
#   {TNS_HOME}
#   {{$path\\tnsnames.ora} }
#   }
#
#   If a particular home as a TNS_ADMIN variable defined as registry variable then
# the corresponding home value in home index will be TNS_HOME.
# i.e. {home1, TNS_HOME, home3 ... homeN} -- here home2 has TNS_ADMIN set in registry
proc nmiconf_collectTnsnamesOraFiles {} {
    set tnsnamesOraFileList {}

    if {[nmiconf_isTNSAdminDefined]} {
        set tnsnamesOraFile [nmiconf_TNSAdminLoc]\\tnsnames.ora
        lappend tnsnamesOraFileList $tnsnamesOraFile
        return $tnsnamesOraFileList
    }

    set oracleHomeEntryList [nmiconf_getAllOracleHomes]

    set tnsnamesOraFile ""
    foreach oracleHomeEntry $oracleHomeEntryList {

        set oracleHome [lindex $oracleHomeEntry 0]
        unset tnsnamesOraFile
        if {[nmiconf_isTNSAdminDefined $oracleHome]} {
            set tnsnamesOraFile [nmiconf_TNSAdminLoc $oracleHome]\\tnsnames.ora
        }

        if {[info exists tnsnamesOraFile] && [file exists $tnsnamesOraFile]} {
            lappend tnsnamesOraFileList $tnsnamesOraFile
        } else {
            # Check for Net80 config file
            set tnsnamesOraFile $oracleHome\\net80\\admin\\tnsnames.ora
            if {[file exists $tnsnamesOraFile] && [lsearch -exact [string toupper $tnsnamesOraFileList] [string toupper $tnsnamesOraFile]] == -1 } {
                lappend tnsnamesOraFileList $tnsnamesOraFile
            }

            # Check for Pre-Net80 config file
            set tnsnamesOraFile $oracleHome\\network\\admin\\tnsnames.ora
            if {[file exists $tnsnamesOraFile] && [lsearch -exact [string toupper $tnsnamesOraFileList] [string toupper $tnsnamesOraFile]] == -1 } {
                lappend tnsnamesOraFileList $tnsnamesOraFile
            }
        }
    }

    return $tnsnamesOraFileList
}

# Reads all the tnsnames.ora files of all the oracle homes and loads the info of all the
# database aliases into the globallook-up table tnsnames
# See nmiconf_collectTnsnamesOraFiles and nmiconf_readTnsnamesOra for more details
proc nmiconf_loadDatabaseAliasInfo {} {

    # The nmiconf_readTnsnamesOra procdure will set the dbalias_list global with the
    # the list of alias names specified in tnsnames.ora files
    global dbalias_list

    nmiconf_beginProcTrace "nmiconf_loadDatabaseAliasInfo"
    nmiconf_trace "Loading database alias information..."
    set tnsnamesOraFileList [nmiconf_collectTnsnamesOraFiles]
    foreach tnsnamesOraFile $tnsnamesOraFileList {
        if { [catch {nmiconf_readTnsnamesOra $tnsnamesOraFile} msg] } {
            nmiconf_warning "Error while parsing $tnsnamesOraFile : $msg"
        }
    }
    nmiconf_endProcTrace "nmiconf_loadDatabaseAliasInfo"
}

# Returns the list of aliases configured for all the databases in
# tnsnames.ora files of this machine
proc nmiconf_getDatabaseAliases {} {
    global dbalias_list
    return $dbalias_list
}

# Validates the listeners by checking whether they have been configured to
# montior for valid SIDs. Invalid SIDs are removed. If a listener is not
# monitoring any valid SID, the listener is removed from the global listener_list
proc nmiconf_validateListeners { listener_list databaseEntries } {
    global listener_sids listener_nameinconfig listener_config

    set databaseSIDs {}
    nmiconf_beginProcTrace "nmiconf_validateListeners"
    nmiconf_trace "Validating list of listeners uncovered vs the list of databases found"
    foreach dbEntry $databaseEntries {
        set dsid [lindex $dbEntry 0]
        lappend databaseSIDs $dsid
    }
    set databaseSIDs [string toupper $databaseSIDs]
    nmiconf_trace "Databases uncovered: \"$databaseSIDs\""

    set validListenerList {}
    set validListenerSIDList {}
    foreach listener $listener_list {
        set validListenerSIDList {}
        foreach lsid $listener_sids($listener) {
            foreach dsid $databaseSIDs {
                if { ![string compare [string toupper $lsid] $dsid] } {
                    lappend validListenerSIDList $lsid
                }
            }
        }
        if {![llength $validListenerSIDList] } {
            nmiconf_warning "$listener_nameinconfig($listener) defined in $listener_config($listener) will be skipped, because it does not monitor any of the valid SIDs \{$databaseSIDs\}"
        } else {
            set listener_sids($listener) $validListenerSIDList
            nmiconf_trace " - listener \"$listener\" is configured for SIDs \"$validListenerSIDList\""
            lappend validListenerList $listener
        }
    }

    nmiconf_trace "[llength $validListenerList] valid listener(s) processed"
    nmiconf_endProcTrace "nmiconf_validateListeners"
    return $validListenerList
}

# Determines whether the given database service name is unique by comparing
# it with the previously known database service names.
proc nmiconf_isDatabaseNameUnique { databaseServiceName } {
    global ServiceNames ServiceType

    foreach serviceName $ServiceNames {
        if { ![string compare $ServiceType($serviceName) ORACLE_DATABASE] } {
            if { ![string compare [nmiconf_getNameFromService $serviceName] $databaseServiceName] } {
                return 0
            }
        }
    }

    return 1
}

# Qualifies the database service name with agent's default
# domain (as specified in sqlnet.ora of agent's home) if the
# name already doesn't contain a domain
proc nmiconf_domainQualifiedName { databaseServiceName } {

    if {![regexp "\[^\\\]\\." $databaseServiceName]} {
        set domainQualifiedName $databaseServiceName[nmiconf_agentDefaultDomain];
    } else {
        # Its already a domain qualified name
        set domainQualifiedName $databaseServiceName
    }

    return $domainQualifiedName
}

# Determine the database service name of the given sid
proc nmiconf_getDatabaseServiceName { sid ora_home ListenerName } {
    global ListenerSuppliedName ListenerNameInConfigFile ListenerConfigFile
    global dbalias_list tnsnames LocalAddress
    global SID

    set ServiceIndex [list $ListenerName ORACLE_LISTENER]
    # Check whether the GLOBAL_DBNAME parameter of monitoring
    # listener can be used as the name
    if {[info exists ListenerSuppliedName([string toupper $sid])]} {

        set listenerDbName [nmiconf_domainQualifiedName $ListenerSuppliedName([string toupper $sid])];
        set listenerDbIndex [list $listenerDbName ORACLE_DATABASE]
        set listenerDbNameUnique [nmiconf_isDatabaseNameUnique $listenerDbName]
        if {$listenerDbNameUnique} {
      # We will not generate our connect string here to contain the SERVICE_NAME parameter
      # in the CONNECT_DATA in order to support backwards compatibility.
            set tnsnames($listenerDbName) "(DESCRIPTION=$LocalAddress($ServiceIndex)(CONNECT_DATA=(SID=$sid)(SERVER=DEDICATED)))";
            return $listenerDbName
        } else {
            # If the GLOBAL_DBNAME is already used, log warning mesg
            # indicating this
            if {!$listenerDbNameUnique} {
                nmiconf_warning "The GLOBAL_DBNAME $ListenerSuppliedName([string toupper $sid]) of listener $ListenerNameInConfigFile($ServiceIndex) defined in $ListenerConfigFile($ServiceIndex) is already used to name the database with sid $SID($listenerDbIndex)"
                nmiconf_warning "It cannot be used to name the database with sid $sid"
            }
        }
    }

    # Check if there is a database alias configured for this sid
  # We need to support the Net8 naming(available for 8i). Net8 supports the SERVICE_NAME parameter
  # as well as the SID parameter in the CONNECT_DATA part of the connect descriptor.
  # We will support SERVICE_NAME if there is a connect descriptor containing it in TNSNAMES.ORA
  # However, when we are generating a connect string to populate the services.ora file we will use SID
  # in the CONNECT_DATA for backwards compatibility.
    foreach dbname $dbalias_list {
    if {[regexp -nocase "SERVICE_NAME\[ \]*=\[ \]*(.*)" $tnsnames($dbname) match rest] || [regexp -nocase "SID\[ \]*=\[ \]*(.*)" $tnsnames($dbname) match rest]} {
     if {[regexp "^\[ \]*$sid\[ \]*\\)" $rest] && [regexp -nocase "\\(\[ \]*protocol\[ \]*=\[ \]*tcp\[ \]*\\)" $tnsnames($dbname)]} {
            if {[nmiconf_isThisHostAddress $tnsnames($dbname)]} {
                set ThisDB $dbname;
                break;
            }
     }
        }
    }
    if {[info exists ThisDB]} {
        # If listener GLOBAL_DBNAME was not used because of name conflict
        # log message indicating that tnsnames alias will be used
        if {[info exists listenerDbNameUnique]} {
            nmiconf_warning "The database alias $ThisDB will be used to name the database with sid $sid"
        }
        return $ThisDB
    }

    # Formulate the name using sid and the host name of the machine
    set ThisDB ${sid}_[nmiconf_agentHostName];
    set ThisDB [nmiconf_domainQualifiedName $ThisDB];
    set tnsnames($ThisDB) "(DESCRIPTION=$LocalAddress($ServiceIndex)(CONNECT_DATA=(SID=$sid)(SERVER=DEDICATED)))";

    return $ThisDB
}

# Procedure to discover databases and listeners
proc nmiconf_discoverListenersAndDatabases {} {
    nmiconf_beginProcTrace "nmiconf_discoverListenersAndDatabases"

    # Listener related globals
    # The nmiconf_loadListenerInfo procedure will set the following globals
    global listener_list listener_address listener_sids ListenerSuppliedName
    global listener_config listener_nameinconfig listener_ntservicename listener_oraclehome

    # Database alias related globals
    # The nmiconf_loadDatabaseAliasInfo procedure will set the following globals
    global tnsnames dbalias_list
    # bug 757783 - define dbalias_list here for third-party discovery with no Oracle DBs.
    set dbalias_list {}

    # The nmiconf_discoverDatabases procedure will set the following globals
    global ServiceNames ServiceType HostName LocalAddress ServiceAddress
    global ListenerShortName ListenerConfigFile ListenerNameInConfigFile ListenerNTServiceName
    global SID OracleHome Listener ProgramName

    set databaseEntries [nmiconf_getDatabaseEntries]
    nmiconf_trace "Only list of valid potential SIDs are: \"$databaseEntries\""

    # Check whether there is atleast one database installed on this machine
    if { ![llength $databaseEntries]} {
        nmiconf_trace "List of database entries found as services is empty!"
        nmiconf_warning "No databases discovered"
        nmiconf_endProcTrace "nmiconf_discoverListenersAndDatabases"
        return
    }

    # load info about all the listeners by reading listener.ora files of all oracle homes.
    nmiconf_loadListenerInfo

    # load info about all the database aliases by reading tnsnames.ora files of all oracle homes.
    nmiconf_loadDatabaseAliasInfo

    set listener_list [nmiconf_validateListeners $listener_list $databaseEntries]

    # fill in the database
    foreach entry $databaseEntries {

        set sid [lindex $entry 0];
        set ora_home [lindex $entry 1]
        set ora_progname [lindex $entry 3]
        nmiconf_trace " - Looking for listener serving \"$sid\" in $ora_home"

        # Find its listener
        foreach listener $listener_list {
            nmiconf_trace "   - Examining Listener \"$listener\""
            nmiconf_trace "     Listener address = $listener_address($listener)"
            nmiconf_trace "     SIDs:"
            foreach lsid $listener_sids($listener) {
                set lsid [string toupper $lsid]
                nmiconf_trace "         $lsid"
                if {($lsid == [string toupper $sid]) && ([regexp -nocase "\\(\[ \]*protocol\[ \]*=\[ \]*tcp\[ \]*\\)" $listener_address($listener)])} {
                    nmiconf_trace "     Listener's SID matched!"
                    if {[info exists ListenerName]} {
                        set ServiceIndex [list $ListenerName ORACLE_LISTENER]
                        nmiconf_warning "Multiple Listeners found for SID $sid."
                        nmiconf_warning "Listener $ListenerNameInConfigFile($ServiceIndex) defined in $ListenerConfigFile($ServiceIndex) will be used"
                        nmiconf_warning "Listener $listener_nameinconfig($listener) defined in $listener_config($listener) will be ignored"
                        break;
                    }
                    set ListenerName "${listener}_[nmiconf_agentHostName]"
                    set ServiceIndex [list $ListenerName ORACLE_LISTENER]
                    nmiconf_trace "   - Storing information for this Listener for discovery"
                    if {![info exists ServiceType($ServiceIndex)]} {
                        lappend ServiceNames $ServiceIndex;
                        set ServiceType($ServiceIndex) ORACLE_LISTENER
                        set HostName($ServiceIndex) [nmiconf_officialHostName]
                        set LocalAddress($ServiceIndex) $listener_address($listener)
                        set ServiceAddress($ServiceIndex) [nmiconf_makeRemoteAddress $listener_address($listener)]
                        set ListenerShortName($ServiceIndex) $listener
                        set ListenerConfigFile($ServiceIndex) $listener_config($listener)
                        set ListenerNameInConfigFile($ServiceIndex) $listener_nameinconfig($listener)
                        set ListenerNTServiceName($ServiceIndex) $listener_ntservicename($listener)
                        set OracleHome($ServiceIndex) $listener_oraclehome($listener)
                    } else {
                        nmiconf_trace "   - The listener $ServiceIndex was already stored away in the list"
                    }
                }
            }
        }

        if {![info exists ListenerName]} {
            nmiconf_warning "No Listener found for SID $sid. $sid will be skipped";
            continue;
        }

        # Find database name
        nmiconf_trace " - Getting a database name for the matching SID"
        set ThisDB [nmiconf_getDatabaseServiceName $sid $ora_home $ListenerName]
        set ServiceIndex [list $ThisDB ORACLE_DATABASE]

        nmiconf_trace " - adding $ServiceIndex to list of discovered databases using:"
        lappend ServiceNames $ServiceIndex
        set ServiceType($ServiceIndex) ORACLE_DATABASE;
        set HostName($ServiceIndex) [nmiconf_officialHostName];
        set Listener($ServiceIndex) $ListenerName;
        set LocalAddress($ServiceIndex) $tnsnames($ThisDB);
        set ServiceAddress($ServiceIndex) [nmiconf_makeRemoteAddress $tnsnames($ThisDB)];
        set SID($ServiceIndex) $sid;
        set OracleHome($ServiceIndex) $ora_home;
        set ProgramName($ServiceIndex) $ora_progname;
        nmiconf_trace "   hostName: $HostName($ServiceIndex)"
        nmiconf_trace "   listener: $Listener($ServiceIndex)"
        nmiconf_trace "   localAddress: $LocalAddress($ServiceIndex)"
        nmiconf_trace "   serviceAddress: $ServiceAddress($ServiceIndex)"
        nmiconf_trace "   oracleHome: $OracleHome($ServiceIndex)"
        nmiconf_trace "   progName: $ProgramName($ServiceIndex)"

        unset ListenerName;
    }
    nmiconf_endProcTrace "nmiconf_discoverListenersAndDatabases"
}

# Returns the oracle home list in a format returned by the nmiconf.tcl script
# This procedure is used only for backward compatibility purposes in
# nmiconf_discoverThirdPartyTargets procedure.
proc nmiconf_getOratabEntries {} {

    set oracleHomeEntryList [nmiconf_getAllOracleHomes]
    set entries {}
    foreach oracleHomeEntry $oracleHomeEntryList {
        set oracleHome [lindex $oracleHomeEntry 0]
        set entry *:$oracleHome
        lappend entries $entry
    }

    return $entries
}

proc nmiconf_eliminateDuplicateServices {} {

    global ServiceNames

    set uniqueServices {}
    foreach service $ServiceNames {
    if {[lsearch $uniqueServices $service] < 0} {
        # First-encountered service of this name/type
        lappend uniqueServices $service
    } else {
        # Duplicate of a previously-encountered service
        nmiconf_warnDuplicateServices $service
    }
    }
    set ServiceNames $uniqueServices
}

proc nmiconf_warnDuplicateServices {service} {
    if {([llength $service] > 1)} {
    nmiconf_warning "Multiple services of type [lindex $service 1] named [lindex $service 0] were discovered.  At least one will be ignored."
    } else {
    nmiconf_warning "Multiple services named [lindex $service 0] were discovered.  At least one will be ignored."
    }
}


# Discover the third party services by invoking the discovery scripts.
# The third party discovery scripts are listed in the file nmiconf.lst
proc nmiconf_discoverThirdPartyTargets {} {
    nmiconf_beginProcTrace "nmiconf_discoverThirdPartyTargets"

    # Define the following global variables of older nmiconf.tcl,
    # used by 3rd party discovery scripts like parallel server and web server.
    #
    # These variables are defined only for backward compatible purposes.
    # Usage of these variables is strongly discouraged.
    # They should not be used by the new third party discovery scripts
    # who want's to integrate with agent.
    #
    # Begin backward compatibility section

    set Host [nmiconf_officialHostName]
    set names [nmiconf_getDatabaseAliases]
    set ORACLE_HOME [nmiconf_agentOracleHome]
    set entries [nmiconf_getOratabEntries]

    # End backward compatibility section

    nmiconf_trace "Discovering third party services..."
    # here's where we call third-party discovery routines
    if {[file exists "nmiconf.lst"]} {
        set ConfigList [open "nmiconf.lst" r]
        while {[gets $ConfigList line] >= 0} {
            set line [string trim $line];
            set length [string length $line];

            if {($length > 0) && ([string index $line 0] != "#")} {
                nmiconf_trace " - discoverying services using \"$line\""
                if [catch {source $line} emesg] {
                    nmiconf_warning "Error while sourcing third party discovery file $line : $emesg"
                }
            }
        }
        close $ConfigList;
    }

    # Final check for duplicate services
    nmiconf_trace "Completed third party discovery"
    nmiconf_eliminateDuplicateServices
    nmiconf_endProcTrace "nmiconf_discoverThirdPartyTargets"
}

# Writes the standar Log banner with the release information
proc nmiconf_logBanner { release } {
    # Make sure the log banner is exactly the same as in nmi.log.
    set logTime [string toupper [clock format [clock seconds] -format "%d-%b-%y %H:%M:%S"]]
    nmiconf_log ""
    nmiconf_log "DBSNMP for 32-bit Windows: release $release - Production on $logTime"

}

# Discover the local host config
proc nmiconf_discoverHost {} {
    nmiconf_beginProcTrace "nmiconf_discoverHost"
    global ServiceNames ServiceType HostName NodeAddress NodeUserData tcl_platform
    global nmiCompatibility
    set host [nmiconf_officialHostName]

    if {![info exists nmiCompatibility] ||
        [string compare $nmiCompatibility 8.1.6] != 0} {
        lappend ServiceNames $host
        set ServiceType($host) ORACLE_NODE
        set HostName($host) $host
        set NodeAddress($host) $host
        set NodeUserData($host) [format "(PLATFORM=(osName=%s)(osVersion=%s))" $tcl_platform(os) $tcl_platform(osVersion)]
        nmiconf_trace "Discovered $host of type ORACLE_NODE"
    }
    nmiconf_endProcTrace "nmiconf_discoverHost"
}


# Start Main

# Turn on debug tracing if enabled
if { [info exists nmiTraceDiscovery] } {
    global nmiconf_traceEnabled
    set nmiconf_traceEnabled 1
} else {
    global nmiconf_traceEnabled
    set nmiconf_traceEnabled 0
}

if { [catch {nmiconf_logBanner 8.1.7} msg] } {
    nmiconf_warning "Failed to generate log banner : $msg"
}

# Add some additional information to the trace to indicate what is being
# discovered
set discoverString [format "Discovering services on %s configured on host addresses %s" [lindex $argv 0] [list [lrange $argv 1 end]]]
nmiconf_log $discoverString

if {![llength [nmiconf_agentNTServiceName]] } {
    nmiconf_error "Agent NT service name is not set"
}

if { [catch {nmiconf_agentOracleHome} msg] } {
    nmiconf_error $msg
}

if {[nmiconf_isFailSafeMode]} {
    nmiconf_log "Agent dicovering in FailSafe mode"
}

# Discover the local host explicitly
if { [catch {nmiconf_discoverHost} msg] } {
    nmiconf_warning "Error while discovering host : $msg"
}

# Discover databases and listeners
nmiconf_trace "Discovering databases and listeners on [nmiconf_getHosts]"
if { [catch {nmiconf_discoverListenersAndDatabases} msg] } {
    nmiconf_warning "Error while discovering listeners and databases : $msg"
}

# Discover third part targets
if { [catch {nmiconf_discoverThirdPartyTargets} msg] } {
    nmiconf_warning "Error while discovering third party targets : $msg"
}

# Generate output files from the discovered info
if { [catch {nmiconf_outputConfigFiles} msg] } {
    nmiconf_error "While generating discovery configuration files : $msg"
}
