#! /bin/bash
# start/stop script for Oracle DB for censhare Server
# status of db may be PRIMARY or physical standby (not PRIMARY) - other modes are not recognized/checked
# compatible with Server-To-Server v2
#
# 2015-May-19 pmo  corrected profile invocation to bash standard
# 2012-Mai-09 pmo  fixed: check for DBGARD_CFG added
# 2011-Mai-24 pmo  added compatibility to Mac OS X
# 2010-Apr-19 pmo  moved create "startup logfile" after check if $ORA_HOME/orahome is available
# 2008-Apr-08 aka  shutdown changed
# 2008-Jan-28 ahu  added gawk path for blaswave - /opt/csw/bin/gawk
# 2007-Oct-05 aka  Copied to CenShare-Server project
# 2007-Aug-22 aka  DataGuard: use of dgmgrl removed, automatically handling with/out DG does work
# 2007-Jul-18 aka  DataGuard NOT implemented - problems with dgmgrl: it blocks at status request
# 2007-Jul-03 aka  current cs2 "rewritten" to cs3
#
# pseudo comments for Novell/SuSE Linux
### BEGIN INIT INFO
# Provides: ora_censhare
# Required-Start: $network sshd
# Required-Stop: $network
# Default-Start: 3 5
# Default-Stop: 0 1 2 6
# Description: Start the oracle database corpus.
### END INIT INFO

# pseudo comments for RedHat / CentOS / Fedora Linux
### BEGIN
# chkconfig: 345 81 10
### END

test -z "$BASH" && exec /bin/bash $0 "$@"		# needed on Solaris-init startup: force bash

progname=$(basename $0)
progname=${progname#rc}
progname=${progname#[KS][0-9][0-9]}
test "$UID" -ne 0  && { echo "$progname: You must be root."; exit 1; }

action="$1"                                             # save action
ec=0                                                    # default: success

# status handling a la LSB-2.x, -3.x
# http://refspecs.freestandards.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html
 ret_other=(success error)                              # return values
ret_status=(running dead_but_run dead_but_lock unused          unknown           reserved5     not_configured)
 ret_start=(started error        inv_args      not_implemented insuff_privileges not_installed not_configured not_running)
  ret_stop=(stopped error        inv_args      not_implemented insuff_privileges not_installed not_configured not_running)
#           0       1            2             3               4                 5             6              7
rc_status () {                                          # inform user
    local s
    case "$action" in
        start)  s=${ret_start[$ec]};;
        stop)   s=${ret_stop[$ec]};;
        status) s=${ret_status[$ec]};;
        *)      test "$ec" = 0 && s=${ret_other[0]} || s=${ret_other[1]};;
    esac
    echo ": ${s:-unknown_exit_code_$ec}"
}

# read configuration
ConfErr () { echo >&2 "$progname: $*"; exit 6; }
cfglist=""						# collect tried pathes, output on error for debugging
ChkCfgfile () {						# first try /etc/sysconfig then /etc/s2s/sysconfig
    local cf="$1"; test -z "$cf" && ConfErr "$FUNCNAME(): filename missing."
    CONFIG_FILE=/etc/sysconfig/$cf;     cfglist="$cfglist $CONFIG_FILE"; test -r "$CONFIG_FILE" && return 0
    CONFIG_FILE=/etc/s2s/sysconfig/$cf; cfglist="$cfglist $CONFIG_FILE"; test -r "$CONFIG_FILE" && return 0
    CONFIG_FILE=/etc/$cf; cfglist="$cfglist $CONFIG_FILE"; test -r "$CONFIG_FILE" && return 0
    return 1						# failed
}
test -z "$CONFIG_FILE" && {				# if no config file defined by environment, look for
    ChkCfgfile $progname.$HOSTNAME || {			#     host specific one or
	ChkCfgfile $progname || {			#     general one or error
	    ConfErr "configuration missing, searched: '${cfglist# }'."; }; }
}							# endif
test -s "$CONFIG_FILE" && . $CONFIG_FILE		# read config
# check for mandatory config vars
test -z "$ORA_USER" && ConfErr "oracle user ORA_USER undefined."
test -z "$ORA_SID"  && ConfErr "oracle sid ORA_SID undefined."
test -z "$DBGARD_CFG" && ConfErr "DBGARD_CFG undefined - check or update config."

# select an appropiate awk
for awk in /opt/csw/bin/gawk /usr/local/bin/gawk /usr/local/bin/awk /usr/xpg4/bin/awk /usr/bin/gawk /usr/bin/awk notfound; do
    if test -x $awk; then break; fi
done
if test $awk = notfound; then echo >&2 "gnu [g]awk not found."; ConfErr "no appropiate awk found on system."; fi

# tmpfile handling
OnExit () {
    set +x
    rm -f $tmpfile $tmpfile.*
}
tmpfile=${TMP:-/tmp}/$progname-$$.tmp
trap OnExit EXIT

# setup/check oracle environment
#ORA_USER=oracle 					# change manually if multiple databases used
#ORA_SID=corpus						# change manually if multiple databases used
eval ORA_HOME=~$ORA_USER				# get/check home dir
test -d $ORA_HOME || ConfErr "user '$ORA_USER' has no home dir, may not mounted."
ORA_PROD=$(ls -1dt $ORA_HOME/orahome 2>/dev/null | tail -1)
test -z "$ORA_PROD" && ConfErr "dir '$ORA_HOME/orahome' not avail, may not mounted."
LOG=$ORA_HOME/.ora_corpus.log				# startup logfile
touch $LOG; chown $ORA_USER $LOG			# prep logfile for oracle user
SQLPLUS="$ORA_HOME/orahome/bin/sqlplus"			# check for sqlplus
test -x $SQLPLUS || ConfErr "oracle '$SQLPLUS' not found."
su - $ORA_USER -c "					# check user environment for configured SID
    if test -r .bash_profile; then . .bash_profile; elif test -r .bash_login; then . .bash_login; elif test -r .profile; then . .profile; fi; $awk -vconf=\"\$ORACLE_SID\" -vprog="$ORA_SID" '
    BEGIN {
        IGNORECASE=1;					# bash-2.05 is not able to do caseinsensitive compares
        if (conf != prog) print conf;			# output ORA_USERs SID to $tmpfile if different
    } ' </dev/null >$tmpfile
" </dev/null >&/dev/null
test -n "$(<$tmpfile)" && \
    echo "Starting $progname: user $ORA_USER configured for SID '$(<$tmpfile)' - wrong SID." && \
    ConfErr "user '$ORA_USER' configured for wrong SID."

dbstat="" dbrole="" dbgard=""				# defaults for getDBStat()

getDBStat () {						# state of database => stdout; ec=0,1,2,3 (running,unused,unknown,mount)
    :							# function considers db role: single, primary, physical standby
    #set -x  						# debugging
    local log=$tmpfile.dbstat
    su - $ORA_USER -c "if test -r .bash_profile; then . .bash_profile; elif test -r .bash_login; then . .bash_login; elif test -r .profile; then . .profile; fi;	# get status and role into local $log file
        echo '$(date +%Y.%m.%d_%H:%M:%S) $progname.getDBStat()' >>$LOG
        echo -e '
select status           from v\$instance;
select database_role    from v\$database;
select dataguard_broker from v\$database;' | tee -a $LOG | $SQLPLUS '/ as sysdba' | tee -a $LOG >$log 2>&1;
    " >&/dev/null </dev/null
    #cat $log >&2    					# debugging
    dbstat=$($awk '/^STATUS/ { getline; getline; print $1 $2; }' $log)
    dbrole=$($awk '/^DATABASE_ROLE/ { getline; getline; print; }' $log)
    dbgard=$($awk '/^DATAGUAR/ { getline; getline; print; }' $log)
    if test "$dbstat" = "OPEN" -a "$dbrole" = "PRIMARY"; then
	ec=0				       		# primary db is open => running
    elif test "$dbstat" = "MOUNTED" -a "$dbrole" = "PHYSICAL STANDBY"; then
	ec=0				       		# physical standby db is mounted => running
    elif test "$dbstat" = "MOUNTED" -a "$dbrole" = "PRIMARY" -a "$dbgard" = "$DBGARD_CFG"; then
	ec=3				       		# primary db without dgbroker is mounted => mount
    elif test "$dbstat" = "ERRORat" -o \( -z "$dbstat" -a -z "$dbrole" -a -z "$dbgard" \) ; then
	ec=1				       		# dbstat is error => unused
    else
        ec=2				       		# other states are unknown
    fi
    echo "($dbstat,$dbrole,$dbgard)"
    return $ec
}

StopDB () {						# stops oracle, returns sqlplus exit code via $tmpfile
    local tmp1=$tmpfile.1
    su - $ORA_USER -c "if test -r .bash_profile; then . .bash_profile; elif test -r .bash_login; then . .bash_login; elif test -r .profile; then . .profile; fi;
        echo '$(date +%Y.%m.%d_%H:%M:%S) $progname.StopDB()' >>$LOG
        echo -e 'connect / as sysdba\nalter system checkpoint;\nalter system switch logfile;\nshutdown abort;\nstartup mount restrict;\nshutdown immediate;' | tee -a $LOG >$tmp1
        $SQLPLUS '/ as sysdba' 2>&1 <$tmp1 >>$LOG 2>&1; echo \$? >$tmpfile
    " >&/dev/null </dev/null;
}

StartupDB () {						# starts oracle and considers role
    su - $ORA_USER -c "if test -r .bash_profile; then . .bash_profile; elif test -r .bash_login; then . .bash_login; elif test -r .profile; then . .profile; fi;	# startup mount
        echo '$(date +%Y.%m.%d_%H:%M:%S) $progname.StartupDB()' >>$LOG
        echo 'startup mount;' | tee -a $LOG | $SQLPLUS '/ as sysdba' 2>&1 | tee -a $LOG;
    " >&/dev/null </dev/null
    local sec=60 dt=2 t=0 stat="" ec=0			# wait up to timeout
    for ((t=sec; t > 0; t=t-dt)); do			# for database become ready...
	stat=$(getDBStat); ec=$?			#     get and save status info
	test $ec = 0 && { echo "$stat"; return 0; }	#     return ok on running
	test $ec = 1 && { echo "$stat"; return 1; }	#     return error on error ;-))
	test $ec = 3 && {				#     if single primary mounted => open it
	    su - $ORA_USER -c "if test -r .bash_profile; then . .bash_profile; elif test -r .bash_login; then . .bash_login; elif test -r .profile; then . .profile; fi;	#         alter database open
        	echo 'alter database open;' | tee -a $LOG | $SQLPLUS '/ as sysdba' 2>&1 | tee -a $LOG;
	    " >&/dev/null </dev/null
	    continue					#         retry status asap
	}						#     endif
	tty >&/dev/null && echo -n "."			# 
	sleep $dt					#     sleep a while
    done						# endfor
    echo "$stat"; return 1;				# return status + error
}

echo -e "\n$(date +%Y.%m.%d_%H:%M:%S) $progname $1" >>$LOG
case "$1" in
    start)
	echo -n "    Starting $progname"
	# check and start only if not already running
	status="$(getDBStat)"; stat=$?			# get instance status
	if test $stat = 0; then				# already open => return ok
	    :
	else
	    if test $stat != 1; then			# strange => try to restart
		echo -n "(curr status='$status')"	#     print state of db to avoid misunderstandings
		StopDB					#     stop before startup
	    fi
	    status="$(StartupDB)"; stat=$?		# normal => try to startup, save status
	fi
	test $stat = 0; ec=$?				# reduce ec to 0..1
	rc_status					# inform user
	;;
    stop)
	echo -n "    Shutting down $progname"
	echo 0 >$tmpfile				# defaults to success
	# check and stop only if already running
	status="$(getDBStat)"; stat=$?			# get instance status
        test $stat != 1 &&				# get instance status and do appropiate
	    StopDB
	test "$(<$tmpfile)" = 0; ec=$?
	rc_status
	;;
    restart)
	## Stop the service and regardless of whether it was
	## running or not, start it again.
	$0 stop; $0 start; ec=$?
	;;
    status)
	echo -n "    Checking for $progname"
	# Status has a slightly different for the status command:
	# 0 - service running
	# 3 - service not running
	status="$(getDBStat)"; stat=$?			# get instance status
        test $stat = 0 && {				# running?
	    ec=0
	} || {
            test $stat = 2 && {				# unknown =>
		echo -n "(curr status='$status')"	#    print state of db to avoid misunderstandings
	    }
            ec=3;
        }
	rc_status
	;;
    *)
	echo "Usage: $0 {start|stop|status|restart}"
	ec=$?
	;;
esac
exit $ec

# Local Variables:
# mode:			shell-script
# mode:			font-lock
# comment-column:	56
# fill-column:		78
# End:
