#!/bin/bash
#
# Copyright 2009-2014 Eucalyptus Systems, Inc.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 3 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see http://www.gnu.org/licenses/.
#
# Please contact Eucalyptus Systems, Inc., 6755 Hollister Ave., Goleta
# CA 93117, USA or visit http://www.eucalyptus.com/licenses/ if you need
# additional information or have any questions.
#
# This file may incorporate work covered under the following copyright
# and permission notice:
#
#   Software License Agreement (BSD License)
#
#   Copyright (c) 2008, Regents of the University of California
#   All rights reserved.
#
#   Redistribution and use of this software in source and binary forms,
#   with or without modification, are permitted provided that the
#   following conditions are met:
#
#     Redistributions of source code must retain the above copyright
#     notice, this list of conditions and the following disclaimer.
#
#     Redistributions in binary form must reproduce the above copyright
#     notice, this list of conditions and the following disclaimer
#     in the documentation and/or other materials provided with the
#     distribution.
#
#   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
#   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
#   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
#   FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
#   COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
#   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
#   BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
#   LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
#   CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
#   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
#   ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
#   POSSIBILITY OF SUCH DAMAGE. USERS OF THIS SOFTWARE ACKNOWLEDGE
#   THE POSSIBLE PRESENCE OF OTHER OPEN SOURCE LICENSED MATERIAL,
#   COPYRIGHTED MATERIAL OR PATENTED MATERIAL IN THIS SOFTWARE,
#   AND IF ANY SUCH MATERIAL IS DISCOVERED THE PARTY DISCOVERING
#   IT MAY INFORM DR. RICH WOLSKI AT THE UNIVERSITY OF CALIFORNIA,
#   SANTA BARBARA WHO WILL THEN ASCERTAIN THE MOST APPROPRIATE REMEDY,
#   WHICH IN THE REGENTS' DISCRETION MAY INCLUDE, WITHOUT LIMITATION,
#   REPLACEMENT OF THE CODE SO IDENTIFIED, LICENSING OF THE CODE SO
#   IDENTIFIED, OR WITHDRAWAL OF THE CODE CAPABILITY TO THE EXTENT
#   NEEDED TO COMPLY WITH ANY SUCH LICENSES OR RIGHTS.
#
# chkconfig: 2345 99 05
# description: eucalyptus java ws services
#
### BEGIN INIT INFO
# Provides:                   eucalyptus
# Required-Start:             $remote_fs $syslog
# Should-Start:               START
# Required-Stop:              $remote_fs $syslog
# Default-Start:              2 3 4 5
# Default-Stop:	              0 1 6
# Short-Description:          Eucalyptus java web services
# Description:                Eucalyptus java web services
# X-UnitedLinux-Default-Enabled: yes
### END INIT INFO
#

# Do NOT "set -e"

# we need to source the current path in case of manual intallation
export PATH=/sbin:/usr/sbin:/bin:/usr/bin:$PATH
DESC="Eucalyptus services"
NAME=""
ANT="`which ant 2> /dev/null`"
EUCA_USER="eucalyptus"
LOCKFILE=/var/lock/subsys/eucalyptus-cloud

if [ "$EUID" != "0" ]; then
	echo "Eucalyptus init scripts must be run as root."
	exit 1
fi

# Use the functions script for distributions that support it (e.g., RHEL)
# This allows Eucalyptus to use correct localization settings
[ -f /etc/init.d/functions ] && . /etc/init.d/functions

# For fault reporting: $LOCALE can be set in /etc/sysconfig/i18n
[ -n "$LOCALE" ] && export LOCALE || unset LOCALE

# if we have lsb functions let's source them
WE_HAVE_LSB="N"
if [ -e /lib/lsb/init-functions ]; then
	. /lib/lsb/init-functions
	# very old lsb don't have the functions we need
	if type log_daemon_msg > /dev/null 2> /dev/null ; then
		WE_HAVE_LSB="Y"
	fi
fi

# honor the ENV variable if found otherwise look in root
if [ -z "$EUCALYPTUS" ] ; then
       EUCALYPTUS="/"
       if [ ! -e ${EUCALYPTUS}/etc/eucalyptus/eucalyptus.conf ] ; then
              EUCALYPTUS="/"
       fi
fi

# Read configuration variable file if it is present
if [ -r $EUCALYPTUS/etc/eucalyptus/eucalyptus.conf ]; then
	EUCA_TMP="`readlink -f ${EUCALYPTUS}`"
	. $EUCALYPTUS/etc/eucalyptus/eucalyptus.conf

	# has eucalyptus been configured?
	if [ "$EUCALYPTUS" = "not_configured" ]; then
		echo "EUCALYPTUS not configured!"
		exit 1
	fi

	# there may be inconsistencied between conf file and env variable
	if [ "$EUCA_TMP" != "`readlink -f ${EUCALYPTUS}`" ]; then
		echo "Warning: you should check EUCALYPTUS in conf file"
	fi
else
	# on removal of RPM we can get in a state in which the conf file
	# is gone but the services are still running: make this hard
	# failure only if we are not stopping
	echo "Cannot find eucalyptus configuration file!"
	if [ "$1" != "stop" ]; then
		exit 1
	fi
fi
# Run additional initialization scripts if present
if [ -d $EUCALYPTUS/etc/eucalyptus/cloud.d/init.d ]; then
		for EUCA_INIT_FILE in $EUCALYPTUS/etc/eucalyptus/cloud.d/init.d/*; do
			if [ -r "${EUCA_INIT_FILE}" ]; then
				# init.d scripts are evaluated but are not permitted to modify
				# environment
				bash "${EUCA_INIT_FILE}" "${@}"
				if [ $? -ne 0 ] ; then
					echo "Initialization script failed: " $(basename "${EUCA_INIT_FILE}")
					exit 1
				fi
			fi
		done
fi
export EUCALYPTUS

pidfile=$EUCALYPTUS/var/run/eucalyptus/eucalyptus-cloud.pid
initlog=$EUCALYPTUS/var/log/eucalyptus/startup.log

check_java_version() {
    local req_major=1
    local req_minor=7
    local error_msg="ERROR: JVM ${req_major}.${req_minor} or newer is required"

    # First check CLOUD_OPTS for --java-home
    JAVA=""
    JH=$( echo $CLOUD_OPTS | \
         sed -r -e '/--java-home/!d; s/^.*(--java-home(=|\s+)(\S+)).*$/\3/g' );
    if [ -n "$JH" ]; then
        if [ -x $JH/jre/bin/java ]; then
            JAVA=$JH/jre/bin/java
        elif [ -x $JH/bin/java ]; then
            JAVA=$JH/bin/java
        fi
    # Next try JAVA_HOME
    elif [ -n "$JAVA_HOME" ]; then
        if [ -x $JAVA_HOME/jre/bin/java ]; then
            JAVA=$JAVA_HOME/jre/bin/java
        elif [ -x $JAVA_HOME/bin/java ]; then
            JAVA=$JAVA_HOME/bin/java
        fi
    fi
    # Last use java from PATH
    if [ -z "$JAVA" ]; then
        JAVA=$( which java )
    fi
    # Compare Java version against requirements
    JV=$( $JAVA -version 2>&1 | \
          sed -r -e '/^java version/!d; s/^java version "(.*)"/\1/' )
    local version=$(echo $JV | cut -d'_' -f1)
    local major=$(echo $version | cut -d'.' -f1)
    local minor=$(echo $version | cut -d'.' -f2)

    if [ "$major" != "$req_major" ] || [ "$minor" -lt "$req_minor" ]; then
        echo "$error_msg" >&2
        exit 1
    fi
}

do_upgrade() {
        while IFS=: read -r a b; do
                OLD_EUCA_VERSION=$a
                OLD_EUCA_PATH=$b
        done < $EUCALYPTUS/etc/eucalyptus/.upgrade

        if [ "$OLD_EUCA_VERSION" = "4.1.0" ]; then
            # No need to upgrade to the same version
            return
        fi

        # Remove old unpacked webapp area
        rm -rf $EUCALYPTUS/var/run/eucalyptus/webapp

        # Remove old class cache
        rm -rf $EUCALYPTUS/var/run/eucalyptus/classcache

        # Verify Java version
        check_java_version

        # Attempt db upgrade
        echo "Attempting database upgrade from $OLD_EUCA_VERSION at $OLD_EUCA_PATH..."
	$EUCALYPTUS/usr/libexec/eucalyptus/euca-upgrade --old $OLD_EUCA_PATH --new $EUCALYPTUS --db --user $EUCA_USER
	case "$?" in
	0)
		echo "done."
		rm -f $EUCALYPTUS/etc/eucalyptus/.upgrade
		;;
        1)
		echo "Upgrade failed. Aborting start."
		exit 1
		;;
	esac
}

do_start() {
	if [ -z "$CLOUD_OPTS" ]; then
		local CLOUD_OPTS="--log-level=INFO"
	fi

	# basic checks
	if [ ! -x $EUCALYPTUS/usr/sbin/euca_conf ]; then
		echo "Some eucalyptus components are missing"
		exit 1
	fi

	# see if we are missing Eucalyptus JARs or have them for more than one version
	COUNT_EUCA_CORE_JARS=`ls -1 ${EUCALYPTUS}/usr/share/eucalyptus/eucalyptus-core-*.jar | wc -l`
	if [ "$COUNT_EUCA_CORE_JARS" -lt 1 ]; then
	    echo "ERROR: Cannot find Eucalyptus JARs in ${EUCALYPTUS}/usr/share/eucalyptus/"
	    exit 1
	fi
	if [ "$COUNT_EUCA_CORE_JARS" -gt 1 ]; then
            echo "ERROR: Found JARs for multiple Eucalyptus versions in ${EUCALYPTUS}/usr/share/eucalyptus/"
            exit 1
        fi

	cd $EUCALYPTUS/etc/eucalyptus

        # Check if we need to upgrade
        if [ -e $EUCALYPTUS/etc/eucalyptus/.upgrade ]; then
                do_upgrade
        fi

        # Add loop devices if necessary (and possible)
        if [ -x "$EUCALYPTUS/usr/share/eucalyptus/create-loop-devices" ]; then
                [ -z "$CREATE_SC_LOOP_DEVICES" ] && CREATE_SC_LOOP_DEVICES=256
                if [ "$CREATE_SC_LOOP_DEVICES" -gt 0 ] 2>/dev/null; then
                    "$EUCALYPTUS/usr/share/eucalyptus/create-loop-devices" $CREATE_SC_LOOP_DEVICES
                fi
        fi

        # check if NTPD is running and throw a fault if it is not
        if [ ! `pgrep -n ntp` ]; then
	    # write fault to stdout
	    echo
	    $EUCALYPTUS/usr/sbin/euca-generate-fault          1008 daemon ntp
	    # and to NC's fault log
	    $EUCALYPTUS/usr/sbin/euca-generate-fault -c cloud 1008 daemon ntp
        fi

	# Check the current euca.p12 creds and backup if they have changed
	check_creds

	ulimit -n 4096
	$EUCALYPTUS/usr/sbin/eucalyptus-cloud $CLOUD_OPTS -h $EUCALYPTUS -u $EUCA_USER --pidfile ${pidfile} -f \
		-L console-log -o $initlog -e $initlog
	RETVAL=$?

	# We should only create a lock file and backup creds if eucalyptus-cloud
	# returns successfully
	if [ $RETVAL -eq 0 ]; then
		touch $LOCKFILE
		backup_creds
	fi

	return $RETVAL
}

do_status() {
    local inpid="$1"
    local ret=0

    if [ -z "$inpid" ]; then
        if [ -s ${pidfile} ]; then
            pid=`cat ${pidfile} 2> /dev/null`
        else
            # no input pid, no pidfile, can't do anything
            return 1
        fi
    else
        pid="$inpid"
    fi

    ppid=`ps -p $pid -o ppid=`
    if [ -n "$ppid" ]; then
        if ! ps axww|grep $ppid|grep eucalyptus-cloud.pid >/dev/null; then
            # pid file is not matching parent pid
            ret=1
        fi
    fi
    if [ -n "$pid" ]; then
        if ! ps axww|grep $pid|grep eucalyptus-cloud.pid > /dev/null; then
            # pid file is not matching
            ret=1
        fi
    fi

    return $ret
}

do_stop() {
	# now stop the service
	if [ -s "${pidfile}" ]; then
		pid=`cat $pidfile 2> /dev/null`
                ppid=`ps -p $pid -o ppid=`
		kill $pid > /dev/null 2>&1
	else
		echo "Warning: eucalyptus-cloud process was not running" >&2
		return 2
	fi

	timeout=120
	while [ $timeout -gt 0 ]; do
		if do_status ; then
			sleep 1
			timeout=$(($timeout - 1))
		else
			break
		fi
	done
	if [ $timeout -eq 0 ]; then
		echo "CRITICAL: timed out waiting for eucalyptus-cloud process to shut down (pid=$pid, ppid=$ppid)." >&2
		echo "CRITICAL: this is most likely a bug; to kill processes (THIS MAY CAUSE DATA LOSS) run: kill -9 $pid $ppid" >&2
		return 1
	fi
	rm -f $pidfile
	rm -f $LOCKFILE

	return 0
}

backup_creds() {
	local credfile="$EUCALYPTUS/var/lib/eucalyptus/keys/euca.p12"
	local backupdir="$EUCALYPTUS/var/lib/eucalyptus/backups/keys"
	local backupcredfile="$backupdir/euca.p12.`date +%y%m%d.%s`"

	[ ! -d "$backupdir" ] && mkdir -p "$backupdir"

	if [ -f "$credfile" ]; then
		local credsum=`sha256sum "$credfile" 2>/dev/null | cut -d' ' -f1`
		local found=false
		for i in "$backupdir"/euca.p12*; do
			if [ "x$credsum" = "x`sha256sum "$i" 2>/dev/null | cut -d' ' -f1`" ]; then
				found=true
				break
			fi
		done

		if ! $found; then
			cp -a "$credfile" "$backupcredfile"
		fi
	fi
}

check_creds() {
	local credfile="$EUCALYPTUS/var/lib/eucalyptus/keys/euca.p12"
	local backupdir="$EUCALYPTUS/var/lib/eucalyptus/backups/keys"
	local backupfile="`ls -1r "$backupdir"/euca.p12* 2>/dev/null | head -n1`"

	# No creds to check
	[ -z "$backupfile" ] && return

	if [ `sha256sum "$credfile" 2>/dev/null | cut -d' ' -f1` != `sha256sum "$backupfile" 2>/dev/null | cut -d' ' -f1` ]; then
		echo "Warning: your database credentials have changed"
		echo "Warning: most recent backup: $backupfile"
	fi
}

# let's get the user to use
if [ -z "$EUCA_USER" ] ; then
	EUCA_USER="root"
fi

# set the library path correctly
if [ -z "$VDDK_HOME" ] ; then
	VDDK_HOME=""
fi
export VDDK_HOME

# moved these to the euca_imager wrapper itself
#export LD_LIBRARY_PATH="$EUCALYPTUS/usr/lib/eucalyptus:$VDDK_HOME/lib:$VDDK_HOME/lib/vmware-vix-disklib/lib32:$VDDK_HOME/lib/vmware-vix-disklib/lib64/" # to ensure euca_imager finds VDDK libs
#export PATH="$EUCALYPTUS/usr/lib/eucalyptus:$PATH" # to ensure euca_imager has euca_rootwrap

#VERBOSE="yes"

case "$1" in
  start)
	if [ "$VERBOSE" != no ]; then
		if [ "$WE_HAVE_LSB" = "Y" ]; then
			log_daemon_msg "Starting $DESC" "$NAME"
		else
			echo -n "Starting $DESC: $NAME"
		fi
	fi

	# let's check there is no previous cloud running
	if do_status ; then
		echo
		echo "$DESC are already running!"
		if [ "$VERBOSE" != no ]; then
			if [ "$WE_HAVE_LSB" = "Y" ]; then
				log_end_msg 1
			fi
		fi
		exit 0
	fi

	do_start

	case "$?" in
	0)
		if [ "$VERBOSE" != no ]; then
			if [ "$WE_HAVE_LSB" = "Y" ]; then
				log_end_msg 0
			else
				echo "done."
			fi
		fi
		;;
	*)
		if [ "$VERBOSE" != no ]; then
			if [ "$WE_HAVE_LSB" = "Y" ]; then
				log_end_msg 1
			else
				echo "failed! Check $initlog for details"
			fi
		fi
		;;
	esac
	;;
  stop)
	retval=0

	if [ "$VERBOSE" != no ]; then
		if [ "$WE_HAVE_LSB" = "Y" ]; then
			log_daemon_msg "Stopping $DESC" "$NAME"
		else
			echo -n "Stopping $DESC: $NAME"
		fi
	fi

	do_stop
	retval=$?

	if [ "$VERBOSE" != no ]; then
		if [ "$WE_HAVE_LSB" = "Y" ]; then
			log_end_msg 0
		else
			echo "done."
		fi
	fi
    
	exit $retval

	;;
  restart)
	$0 stop
	$0 start
	;;
  status)
	if do_status ; then
		echo "$DESC are running"
	else
		exit 3
	fi
	;;
  *)
	echo "Usage: $0 {start|stop|restart}" >&2
	exit 3
	;;
esac
