# $Id: ora-smf-func,v 1.32.2.1 2009/06/29 10:56:50 joost Exp $ # # This file contains functions for the ora-smf method. It is sourced by # $MTHPATH/ora-smf. # # # The contents of this file are subject to the terms of the # Common Development and Distribution License, Version 1.0 only # (the "License"). You may not use this file except in compliance # with the License. # # You can obtain a copy of the license at # http://www.opensource.org/licenses/cddl1.php # See the License for the specific language governing permissions # and limitations under the License. # # Copyright 2005, Joost Mulders. # # # Some globals FMRI's and path's # # The main location of files are controlled by MFTPATH (manifests) and # MTHPATH (method) variables. These are defined in the package (pkginfo) and # exported in the environment by the ora-smf method. # [[ ! -z $MFTPATH ]] || MFTPATH=$(/usr/bin/pkgparam ora-smf MFTPATH) [[ ! -z $MTHPATH ]] || MTHPATH=$(/usr/bin/pkgparam ora-smf MTHPATH) db_fmri=svc:/application/oracle/database lr_fmri=svc:/application/oracle/listener dbs_mft=$MFTPATH/oracle-database-service.xml dbi_mft=$MFTPATH/oracle-database-instance.xml lrs_mft=$MFTPATH/oracle-listener-service.xml lri_mft=$MFTPATH/oracle-listener-instance.xml otab=/var/opt/oracle/oratab # # Start/Stop database # control_database () { local operation=$1 local instance=$2 local authvar="" local fmri="$db_fmri:$instance" local propname="options/auth" local authval="" local dbstatus="" local oere="" local processcount=0 local shutdown_method="immediate" local status=$SMF_EXIT_ERR_FATAL local sqlinit="set linesize 2048 feedback off \ verify off heading off echo off wrap off" if [[ $operation != start && $operation != stop || -z $instance ]]; then echo "Usage: control_database " return $status fi # # Get the connect auth string from the repository # svcprop should have a flag to turn *on* escaping # authval=$(eval echo $(/usr/bin/svcprop -c -p $propname $fmri)) if (( $? != 0 )) || [[ -z $authval ]]; then echo "Could not read $propname from $fmri" return $status fi if [[ $operation == stop ]]; then # # We need to check here if the instance is really running. In case of a fault # or other instance destruction, smf(5) will execute the stop-method and then # the start-method. We must make sure not to hang in trying to stop such a # destroyed instance. We determine the state of the instance by counting the # instance processes. If it is below the bare minimum, something bad happened. # # oere is a regular expression describing the minimum instance processes: # (Note:153655.1, verified for 8.1.6, and for 10.1.0) # lgwr: logwriter # dbw0: database writer. There may be more but at least '0' should be there # smon: system monitor # pmon: process monitor # reco: recovery # ckpt: check point # # If one or more of these processes are gone, we abort the instance. # oere="^ora_(lgwr|dbw0|smon|pmon|reco|ckpt)_$instance" processcount=$(( $(/usr/bin/pgrep -f -z $(/usr/bin/zonename) "$oere" | \ /usr/bin/wc -l) )) if (( $processcount < 6 )); then # # Abort the instance. # shutdown_method="abort" # # Visiualize this happening # echo "*********************************************************************" echo "*********************************************************************" echo "** some of '$oere' died." echo "** Aborting instance $instance." echo "*********************************************************************" echo "*********************************************************************" fi echo "$sqlinit\n connect $authval\n shutdown $shutdown_method\n" | database_command status=$? elif [[ $operation == start ]]; then echo "$sqlinit\n connect $authval\n startup\n" | database_command status=$? # # Verify that the database really delivers service. SMF couldn't care less. # if (( $status == 0 )); then dbstatus=$(echo "$sqlinit\n connect $authval\n select status from v\$instance;" | database_command) if [[ $dbstatus != OPEN ]]; then echo "Instance $instance not OPEN. State is '$dbstatus'." status=$SMF_EXIT_ERR_FATAL else echo "database $instance is OPEN." # for business ;-) fi fi fi return $status } control_listener () { local operation=$1 local instance=$2 local status=$SMF_EXIT_OK if [[ $operation == stop ]]; then # # We must make sure here the listener is really running. If the listener is # not running due to some fault, the stop method fails and smf puts the # service in maintenance. # $ORACLE_HOME/bin/lsnrctl status $instance if (( $? != 0 )); then # Something bad happened to the listener. It is not delivering service # Tell SMF we're already stopped. return $SMF_EXIT_OK fi fi # # Start/Stop the listener. # $ORACLE_HOME/bin/lsnrctl $operation $instance if (( $? == 0 )); then echo "listener $instance $operation succeeded" else echo "listener $instance $operation failed" status=$SMF_EXIT_ERR_FATAL fi return $status } # # Feed database statements from stdin into SQL*Plus, separate errors and # strip blur. # database_command () { local cmd_error_file=/tmp/ora-smf.$$ local cmd_status=$SMF_EXIT_OK $ORACLE_HOME/bin/sqlplus -s /nolog | /usr/bin/sed -e " /Connected\.\$/d /^[ ]*\$/d s/^[ ]*// s/[ ]*\$//g /^\$/d /^ORA-[0-9]\{5,\}:.*\$/w $cmd_error_file " if (( $? != 0 )); then # It didn't run cmd_status=$SMF_EXIT_ERR_FATAL elif [[ -r $cmd_error_file ]] && \ (( $(( $(/usr/bin/wc -l < $cmd_error_file) )) > 0)); then /usr/bin/rm -f $cmd_error_file cmd_status=$SMF_EXIT_ERR_FATAL fi return $cmd_status } # # Sanity check on the arguments received # check_arguments () { local argok=0 if (( $# == 3 )); then if [[ $1 == start || $1 == stop ]]; then if [[ $2 == listener || $2 == database ]]; then argok=1 fi fi elif (( $# == 1 )) && [[ $1 == autoimport ]]; then argok=1 fi if (( $argok == 0 )); then echo "Usage: $0 " return $SMF_EXIT_ERR_FATAL else return $SMF_EXIT_OK fi } # # If we have 'database', we need ORACLE_HOME + ORACLE_SID # If we have 'listener', we need ORACLE_HOME # check_environ () { local envok=0 if [[ ! -z $ORACLE_HOME && -d $ORACLE_HOME ]]; then if [[ $2 == database && ! -z $ORACLE_SID || $2 == listener ]]; then envok=1 fi fi if (( $envok == 0 )); then echo "Missing ORACLE_HOME, ORACLE_SID in the environment" return $SMF_EXIT_ERR_FATAL else return $SMF_EXIT_OK fi } ################################################################################ # Functions related to the autoimport 'feature' of the ora-smf method ################################################################################ # # print the oratab file and expand the '*' if it is there. # cat_otab () { local f="" local sid="" local ohome="" local start="" if [[ ! -r $otab ]]; then echo "'$oratab' not found" return 1 fi # # print things that look like a home spec. # /usr/bin/nawk -F':' '!/^#/ && /.*:.*:[YNyn].*/ { printf ("%s %s %s\n", $1, $2, $3) }' < $otab | \ while read sid ohome start do if [[ $sid == '*' ]]; then # # expand the asterisk by looking in $ohome/dbs/init*.ora # for f in $(/usr/bin/ls $ohome/dbs/init*.ora 2>/dev/null) do f=$(/usr/bin/basename $f | /usr/bin/sed -e ' s/^init// s/\.ora$//') if [[ ! -z $f && $f != dw && $f != sample && \ -f $ohome/dbs/init${f}.ora ]]; then # check not a dangling link /usr/bin/printf "%-12s %-40s %-5s\n" $f $ohome $start fi done else /usr/bin/printf "%-12s %-40s %-5s\n" $sid $ohome $start fi done | /usr/bin/sort -u -b -k 2 -k 1 } # # Read the database instance manifest and modify it with local value's # make_db_instance_manifest () { local sid=$1 local ohome=$2 local autostart=$3 local ouser=$4 local ogroup=$5 local mthpath="" local mftpath="" # # prefix the slashes in pathnames with a '\' so we can use it as a sed # replacement string # ohome=$(echo $ohome | /usr/bin/sed -e 's/\//\\\//g') /usr/bin/sed -e " # replace the instance name // { s/name=.* /name='$sid' / } # change the user and group in method_credentials // { s/user=.*/user='$ouser' / s/group=.*/group='$ogroup' / } # change the method_environment //,/<\/method_environment>/ { // { s/name='ORACLE_SID' value='.*'/name='ORACLE_SID' value='$sid'/ s/name='ORACLE_HOME' value='.*'/name='ORACLE_HOME' value='$ohome'/ } } " < $dbi_mft } auto_import_database () { local istatus=0 # import exit status local isidcount=0 # no. of sid's in oratab local isidsucces=0 # no. of sid's imported succesfully local isidfailure=0 # no. of sid's for which import failed local isidexist=0 # no. of sid's that already existed in the repository local sid="" local ohome="" local start="" local oraowner="" local oragp="" local fmri="" if [[ -r $otab ]]; then # # First, import the service manifest if this is not already done so # /usr/sbin/svccfg -s $db_fmri < /dev/null > /dev/null 2>&1 if (( $? != 0 )); then /usr/sbin/svccfg import $dbs_mft if (( $? != 0 )); then echo "Import of $dbs_mft failed." return 1 fi fi cat_otab | while read sid ohome start do isidcount=$(( $isidcount + 1 )) /usr/bin/printf "%15s: " "$sid" # # find the owner of this oracle software; # if [[ -f ${ohome}/bin/oracle ]]; then /usr/bin/ls -l ${ohome}/bin/oracle | \ /usr/bin/nawk '{print $3 " " $4 }' | read oraowner oragp if [[ ! -z $oraowner && ! -z $oragp ]]; then # # Do the import only if the sid does not exist. # fmri="$db_fmri:$sid" /usr/bin/svcs $fmri > /dev/null 2>&1 if (( $? == 1 )); then # # This is a new sid. Create a manifest and import it. # make_db_instance_manifest \ $sid $ohome $start $oraowner $oragp | \ /usr/sbin/svccfg import - if (( $? == 0 )); then echo "Imported as $fmri" isidsucces=$(( $isidsucces + 1 )) else echo "Failed." isidfailure=$(( $isidfailure + 1 )) fi else echo "Already imported." isidexist=$(( $isidexist + 1 )) fi else echo "Failed: Could not get owner/group from ${ohome}/bin/oracle" isidfailure=$(( $isidfailure + 1 )) fi else echo "Failed: $ohome/bin/oracle not executable." isidfailure=$(( $isidfailure + 1 )) fi done else echo "Could not read $otab" fi } # # Read the listener instance manifest and modify it with local value's # make_lr_instance_manifest () { local lsnrname="$1" local ohome="$2" local ouser="$3" local ogroup="$4" # # prefix the slashes in ohome with a '\' so we can use it as a sed # replacement string # ohome=$(echo $ohome | sed -e 's/\//\\\//g') /usr/bin/sed -e " # replace the instance name // { s/name=.* /name='$lsnrname' / } # change the user and group in method_credentials // { s/user=.*/user='$ouser' / s/group=.*/group='$ogroup' / } # change the method_environment //,/<\/method_environment>/ { // { s/name='ORACLE_HOME' value='.*'/name='ORACLE_HOME' value='$ohome'/ } } " < $lri_mft } auto_import_listener () { local ohome="" local lsrnrcfg="" local lsnrname="" local ohome="" local ouser="" local ogroup="" local fmri="" local ilrcount=0 # # of listeners found local ilrsucces=0 # # of listeners imported local ilrfailure=0 # # of listener import failures local ilrexist=0 # # of listeners that already existed local fmri="" # # First, import the service manifest if not already done so # /usr/sbin/svccfg -s $lr_fmri /dev/null 2>&1 if (( $? != 0 )); then /usr/sbin/svccfg import $lrs_mft if (( $? != 0 )); then echo "Import of $lrs_mft failed." return 1 fi fi # # Make a list of unique ORACLE_HOME's and peel the listeners from their # listener.ora # for ohome in $(cat_otab | /usr/bin/nawk '{print $2}' | /usr/bin/sort -u) do if [[ -r $ohome/network/admin/listener.ora ]]; then for lsnrname in $(/usr/bin/nawk ' BEGIN { FS="[ \t=]"; } # # This should be better. Right now we mis listener names with underscores # in them. # $1 ~ /^[a-zA-Z]+/ && $1 !~ /_/ { print $1 } ' < $ohome/network/admin/listener.ora) do /usr/bin/printf "%15s: " "$lsnrname" ilrcount=$(( $ilrcount + 1 )) # # Find the user and group for this listener. We use the credentials from # ohome/bin/oracle for this. I'm not sure this is right but it's a # starter. # if [[ -r $ohome/bin/oracle ]]; then /usr/bin/ls -l $ohome/bin/oracle | \ /usr/bin/nawk '{print $3 " " $4 }' | read ouser ogroup if [[ ! -z $ouser && ! -z $ogroup ]]; then # # See if this listener is already in the repository # fmri="$lr_fmri:$lsnrname" /usr/bin/svcs $fmri > /dev/null 2>&1 if (( $? == 1 )); then make_lr_instance_manifest \ $lsnrname $ohome $ouser $ogroup | \ /usr/sbin/svccfg import - if (( $? == 0 )); then echo "Imported as $fmri" ilrsucces=$(( $ilrsucces + 1 )) else echo "Failed." ilrfailure=$(( $ilrfailure + 1 )) fi else echo "Already exists." ilrexist=$(( $ilrexist + 1 )) fi else echo "Failed." ilrfailure=$(( $ilrfailure + 1 )) fi else echo "Failed." ilrfailure=$(( $ilrfailure + 1 )) fi done fi # -r listener.ora done # for each ohome } # # Remove all ora-smf stuff from the repository # delete_oracle_cfg () { set -x local fmri="" local ionline=0 # instance's online count local svc="" for svc in $db_fmri $lr_fmri do # # Remove instances if they are not online # ionline=0 for fmri in $(/usr/bin/svcs -H -o FMRI $svc:\* 2>/dev/null) do # no delete if online if [[ $(/usr/bin/svcs -H -o STATE $fmri) == online ]]; then echo "$fmri is online. Please disable first." ionline=$(( $ionline + 1 )) else /usr/sbin/svccfg delete $fmri && echo "deleted $fmri" fi done # # Remove the service if all instances are removed # if (( $ionline == 0 )); then svccfg delete $svc > /dev/null 2>&1 && echo "deleted $svc" # # remove the dependencies created in multi-user-server # echo " select svc:/milestone/multi-user-server:default delprop oracle-database_multi-user-server delprop oracle-listener_multi-user-server" | \ /usr/sbin/svccfg -f - > /dev/null 2>&1 fi done return $ionline }