How do I identify and set my environment for the Oracle Database and Clusterware homes on a server?

by Seth Miller

When Oracle software is installed on a server, it generally has a single home referred to as the oracle home. The installer will usually add the oracle home to the central inventory. The location of the central inventory can be determined by querying an operating system specific oracle inventory location file. On Linux, this location file is /etc/oraInst.loc. If you ever installed the Oracle database software and wondered why you need to run a root script at the end of the installation, this is one of the files created by root because an unprivileged user does not have the ability to write to the /etc directory.

The file in the central inventory with the information about the oracle homes is <Inventory Location>/ContentsXML/inventory.xml. As you can see from the file extension, this file is in the XML format. Here is an example of an oracle inventory file from a server that has a single installation of Oracle Database 11.2.0.4.

<?xml version="1.0" standalone="yes" ?>
<!-- Copyright (c) 1999, 2013, Oracle and/or its affiliates.
All rights reserved. -->
<!-- Do not modify the contents of this file by hand. -->
<INVENTORY>
<VERSION_INFO>
   <SAVED_WITH>11.2.0.4.0</SAVED_WITH>
   <MINIMUM_VER>2.1.0.6.0</MINIMUM_VER>
</VERSION_INFO>
<HOME_LIST>
<HOME NAME="Ora_home1" LOC="/u01/app/oracle/product/11.2.0/dbhome_1" TYPE="O" IDX="1"/>
</HOME_LIST>
<COMPOSITEHOME_LIST>
</COMPOSITEHOME_LIST>
</INVENTORY>

It should be fairly obvious that the LOC attribute of the HOME element is the directory path of that oracle home. What isn’t so obvious is what the home contains.

In fact, if we look at the inventory file of a server with a database home as well as a clusterware home, it’s not clear which is which. We could infer that the home containing the word grid is a clusterware home and the home containing the word dbhome is the database home but those directory names are chosen by the user and could be something completely different.

<?xml version="1.0" standalone="yes" ?>
<!-- Copyright (c) 1999, 2013, Oracle and/or its affiliates.
All rights reserved. -->
<!-- Do not modify the contents of this file by hand. -->
<INVENTORY>
<VERSION_INFO>
   <SAVED_WITH>11.2.0.4.0</SAVED_WITH>
   <MINIMUM_VER>2.1.0.6.0</MINIMUM_VER>
</VERSION_INFO>
<HOME_LIST>
<HOME NAME="Ora_home1" LOC="/u01/app/11.2.0/grid" TYPE="O" IDX="1"/>
<HOME NAME="Ora_home2" LOC="/u01/app/oracle/product/11.2.0/dbhome_1" TYPE="O" IDX="2"/>
</HOME_LIST>
<COMPOSITEHOME_LIST>
</COMPOSITEHOME_LIST>
</INVENTORY>

In order to identify what type of installation is contained in an oracle home, we have to look at the inventory contained within the oracle home. Each of the oracle homes listed in the central inventory will have a <Oracle Home>/inventory/ContentsXML/comps.xml file. These local inventory files are much, much larger than the central inventory file. The information about the installation that we are looking for is right up at the top of the file in the NAME attribute of the COMP element which looks like this.

<COMP NAME="oracle.server" VER="11.2.0.4.0" BUILD_NUMBER="0" REP_VER="0.0.0.0.0" RELEASE="Production" INV_LOC="Components/oracle.server/11.2.0.4.0/1/" LANGS="en" XML_INV_LOC="Components21/oracle.server/11.2.0.4.0/" ACT_INST_VER="11.2.0.4.0" DEINST_VER="11.2.0.0.0" INSTALL_TIME="2015.Dec.14 12:51:59 EST" INST_LOC="/u01/app/oracle/product/11.2.0/dbhome_1/oracle.server">

There are dozens of these COMP elements in the file so we need to make sure the one we’re looking at is nested under the TL_LIST which is nested under the PRD_LIST root element. If we looked at this in xpath notation, it would be /PRD_LIST/TL_LIST/COMP/@NAME. The attribute location is the same for database and clusterware homes. The value will either be oracle.crs or oracle.server, the former being a clusterware home and the latter being a database home.

Now that we know where the information is, we can use a script to gather that information into a variable. The find_homes function creates an associative array called HOMES_ARR that holds each oracle home directory path and its home type.

function find_homes
{
  unset global HOMES_ARR
  declare -g -A HOMES_ARR
  local LIST_OF_HOMES=($(echo "cat //HOME[contains(@TYPE, \"O\")]/@LOC" | \
                         xmllint --shell \
                         $(grep '^inventory_loc=' /etc/oraInst.loc | \
                           cut -d= -f2)/ContentsXML/inventory.xml | \
                           grep 'LOC=' | \
                           sed 's/.*="\([^"]\+\)"/\1/'))
  for MYHOME in "${LIST_OF_HOMES[@]?}"; do
    local HOME_TYPE=$(echo "cat /PRD_LIST/TL_LIST/COMP/@NAME" | \
                      xmllint --shell $MYHOME/inventory/ContentsXML/comps.xml | \
                      grep 'NAME=' | \
                      sed 's/.*="\([^"]\+\)"/\1/')
    HOMES_ARR["${MYHOME?}"]="${HOME_TYPE}"
  done
}

If we run this on the server containing the second central inventory from above that has both a clusterware and database home, I can then loop through the HOMES_ARR variable and see the information for each home.

function homes_loop
{
  find_homes
  for MYHOME in ${!HOMES_ARR[@]}; do
    echo "Oracle home ${MYHOME} is an "${HOMES_ARR["${MYHOME}"]}" home"
  done
}
 
 
[oracle@localhost ~]$ homes_loop
Oracle home /u01/app/oracle/product/11.2.0/dbhome_1 is an oracle.server home
Oracle home /u01/app/11.2.0/grid is an oracle.crs home

The oraenv script does a really good job of setting the environment of the OS user to that of a database instance.

[oracle@localhost ~]$ grep -v '^#\|^$' /etc/oratab
orcl:/u01/app/oracle/product/11.2.0/dbhome_1:N
 
[oracle@localhost ~]$ . oraenv
ORACLE_SID = [oracle] ? orcl
The Oracle base remains unchanged with value /u01/app/oracle
 
[oracle@localhost ~]$ export ORAENV_ASK=NO
[oracle@localhost ~]$ export ORACLE_SID=orcl
[oracle@localhost ~]$ . oraenv
The Oracle base remains unchanged with value /u01/app/oracle

However, unless ASM is installed on a server, setting the environment for the clusterware home is not very straight forward. We could still use oraenv, but then we would need to either manually type or copy/paste the entire path of the clusterware home.

[oracle@localhost ~]$ . oraenv
ORACLE_SID = [oracle] ?
ORACLE_HOME = [/home/oracle] ? /u01/app/11.2.0/grid
The Oracle base has been set to /u01/app/oracle

Since we can identify the directory path of the clusterware home from the HOMES_ARR variable, we can take advantage of the many things oraenv does without having to have an entry for the clusterware home in the oratab.

function grid
{
  find_homes
  for MYHOME in ${!HOMES_ARR[@]}; do
    local HOMETYPE=${HOMES_ARR["${MYHOME}"]}
    if [ "${HOMETYPE}" = "oracle.crs" ]; then
      sed -e 's/${ORAENV_ASK:-""}/"NO"/' -e 's%^\s*ORAHOME=.*%ORAHOME="'${MYHOME}'"%' $(which oraenv) > /tmp/oraenv$PPID
      . /tmp/oraenv$PPID
      rm /tmp/oraenv$PPID
      export GRID_HOME="${MYHOME}"
    fi
  done
}

This grid function in combination with the find_homes function can be put into a profile script and used by multiple users on the database servers without having to hard code any paths.