#!/bin/bash
#
# Copyright (c) 2011 Eliot Gable <egable@gmail.com>
#
# The MIT License
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# 
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
# 
#######################################################################
# Initialization:

: ${OCF_FUNCTIONS_DIR=${OCF_ROOT}/resource.d/heartbeat}
. ${OCF_FUNCTIONS_DIR}/.ocf-shellfuncs

unset LC_ALL; export LC_ALL
unset LANGUAGE; export LANGUAGE
LC_ALL=C; export LC_ALL
LC_MESSAGES=C; export LC_MESSAGES

SENDARP=$HA_BIN/send_arp

# Defaults
OCF_RESKEY_ips_default=""
: ${OCF_RESKEY_ips=${OCF_RESKEY_ips_default}}

OCF_RESKEY_recover_calls_default="false"
: ${OCF_RESKEY_recover_calls=${OCF_RESKEY_recover_calls_default}}

OCF_RESKEY_fsroot_default="/usr/local/freeswitch"
: ${OCF_RESKEY_fsroot=${OCF_RESKEY_fsroot_default}}

OCF_RESKEY_fs_cli_path_default="${OCF_RESKEY_fsroot}/bin/fs_cli"
: ${OCF_RESKEY_fs_cli_path=${OCF_RESKEY_fs_cli_path_default}}

OCF_RESKEY_user_default="freeswitch"
: ${OCF_RESKEY_user=${OCF_RESKEY_user_default}}

OCF_RESKEY_group_default="freeswitch"
: ${OCF_RESKEY_group=${OCF_RESKEY_group_default}}

OCF_RESKEY_priority_default="normal"
: ${OCF_RESKEY_priority=${OCF_RESKEY_priority_default}}

OCF_RESKEY_natdetect_default="nonat"
: ${OCF_RESKEY_natdetect=${OCF_RESKEY_natdetect_default}}

OCF_RESKEY_nocal_default="true"
: ${OCF_RESKEY_nocal=${OCF_RESKEY_nocal_default}}

OCF_RESKEY_nort_default="false"
: ${OCF_RESKEY_nort=${OCF_RESKEY_nort_default}}

OCF_RESKEY_coredir_default="/var/core/freeswitch"
: ${OCF_RESKEY_coredir=${OCF_RESKEY_coredir_default}}

OCF_RESKEY_confdir_default=""
: ${OCF_RESKEY_confdir=${OCF_RESKEY_confdir_default}}

OCF_RESKEY_logdir_default=""
: ${OCF_RESKEY_logdir=${OCF_RESKEY_logdir_default}}

OCF_RESKEY_dbdir_default=""
: ${OCF_RESKEY_dbdir=${OCF_RESKEY_dbdir_default}}

OCF_RESKEY_rundir_default=""
: ${OCF_RESKEY_rundir=${OCF_RESKEY_rundir_default}}

OCF_RESKEY_moddir_default=""
: ${OCF_RESKEY_moddir=${OCF_RESKEY_moddir_default}}

OCF_RESKEY_htdocsdir_default=""
: ${OCF_RESKEY_htdocsdir=${OCF_RESKEY_htdocsdir_default}}

OCF_RESKEY_scriptsdir_default=""
: ${OCF_RESKEY_scriptsdir=${OCF_RESKEY_scriptsdir_default}}


if [ ! -d "/var/run/freeswitch" ]
then
	mkdir -p /var/run/freeswitch/
	if [ ! -d "/var/run/freeswitch" ]
	then
		return $OCF_ERR_GENERIC
	fi
fi
#######################################################################

usage() {
	echo 'Usage: FreeSWITCH start|stop|monitor|meta-data|validate-all|promote|demote|notify'
	exit 1
}

meta_data() {
	cat <<END
<?xml version="1.0"?>
<!DOCTYPE resource-agent SYSTEM "ra-api-1.dtd">
<resource-agent name="FreeSWITCH">
<version>3.0</version>

<longdesc lang="en">
This is the FreeSWITCH resource agent. It manages FreeSWITCH and the set of
IP addresses used in your profiles. It is designed to handle only a master/slave
set of nodes or a single node. It does not support handling a clone set where there
is more than one active (master) node at a time.
</longdesc>
<shortdesc lang="en">FreeSWITCH resource agent</shortdesc>

<parameters>

<parameter name="ips" unique="1" required="1">
<longdesc lang="en">
This parameter lists the devices and IP addresses that are devoted to
FreeSWITCH. The parameter takes a colon-delimited list of dev/ip/cidr trios,
which collectively specify the IP addresses that FreeSWITCH requires to be
instated before executing and which control the master/slave state. Example:
ips=eth2/10.10.5.100/24:eth3/172.16.88.5/28:eth3/172.16.88.6/28

FreeSWITCH will start without the IP addresses present on the system. When
a particular instance of FreeSWITCH is promoted to master, that instance will
bring up the IP addresses. It will keep the IP addresses until it is demoted.
Each time an interface is brought up, the RA will attempt to re-instate routes
for the interface which just received an IP. This way, if routes depend on
these IP addresses being present, they will come up when the IP address is
added. Currently, this only works on RedHat-based systems (including CentOS).
</longdesc>
<shortdesc lang="en">
</shortdesc>
<content type="string" default="${OCF_RESKEY_ips_default}" />
</parameter>

<parameter name="recover_calls" unique="0" required="0">
<longdesc lang="en">
FreeSWITCH is capable of performing call recovery during a fail over event. To
achieve this mode of operation, you must have built FreeSWITCH with core ODBC
support, you must have FreeSWITCH on both nodes configured to write all call
state information to a shared database which is accessible when either node
fails, and you must have configured the hostname in switch.conf.xml to the
same value on both nodes. It would also be wise to host your database in HA
mode on at least a pair of nodes, if not an entire cluster. During the fail over,
calls may lose audio for a brief duration. This is not guaranteed to recover all
calls. Calls being set up and calls behind some types of NAT routers will be
dropped. Also, interference from anti-DoS firewalls/tools could cause issues
recovering all calls.  
</longdesc>
<shortdesc lang="en">
Determines whether FreeSWITCH will attempt to recover calls during a fail over.
</shortdesc>
<content type="boolean" default="${OCF_RESKEY_recover_calls_default}" />
</parameter>

<parameter name="fsroot" unique="0" required="0">
<longdesc lang="en">
Tells us where to find the FreeSWITCH root folder.
</longdesc>
<shortdesc lang="en">
Tells us where to find the FreeSWITCH root folder.
</shortdesc>
<content type="string" default="${OCF_RESKEY_fsroot_default}" />
</parameter>

<parameter name="fs_cli_path" unique="0" required="0">
<longdesc lang="en">
This specifies the path to the fs_cli executable.
</longdesc>
<shortdesc lang="en">
This specifies the path to the fs_cli executable.
</shortdesc>
<content type="string" default="\${fsroot}/bin/fs_cli" />
</parameter>

<parameter name="user" unique="0" required="0">
<longdesc lang="en">
User to switch to after starting FS.
</longdesc>
<shortdesc lang="en">
User to switch to after starting FS.
</shortdesc>
<content type="string" default="${OCF_RESKEY_user_default}" />
</parameter>

<parameter name="group" unique="0" required="0">
<longdesc lang="en">
Group to switch to after starting FS.
</longdesc>
<shortdesc lang="en">
Group to switch to after starting FS.
</shortdesc>
<content type="string" default="${OCF_RESKEY_group_default}" />
</parameter>

<parameter name="priority" unique="0" required="0">
<longdesc lang="en">
This parameter sets the priority mode for FreeSWITCH. Options are 'realtime',
'low', and 'normal' (default). Real time priority makes FreeSWITCH run with the
highest priority possible. In general, the Linux schedular will provide it
with CPU resources ahead of nearly all other processes on the system. Low
priority is the opposite. FreeSWITCH's threads will tend to be defered in
preference of other processes and threads running on the system. By default,
FreeSWITCH starts with normal priority, which means it is evenly weighted
with other processes and threads on the system.
</longdesc>
<shortdesc lang="en">
This parameter sets the priority mode for FreeSWITCH. Options are realtime,
low, and normal (default). 
</shortdesc>
<content type="string" default="${OCF_RESKEY_priority_default}" />
</parameter>

<parameter name="natdetect" unique="0" required="0">
<longdesc lang="en">
During start-up, FreeSWITCH can automatically detect if you are behind a NAT. This
process can take up to 10 seconds to complete and adds to the start-up time before
FreeSWITCH is ready to answer requests. In most HA environments, FreeSWITCH is
assigned static IP addresses, so this automatic NAT resolution is usually not
only pointless for HA systems, but also can negatively impact availability due to
the extended start-up times. Thus, the default setting is 'nonat'. The mechanism 
of discovery is NAT-PMP or UPnP. The device performing the NAT must support one 
of these mechanisms for this feature to work. Alternatively you may leave NAT 
detection on, but disable just the port mapping aspect of the automatic NAT 
detection by specifying 'nonatmap'. To enable full NAT detection with NAT port-mapping,
set this to 'autonat'.
</longdesc>
<shortdesc lang="en">
Disable automatic NAT resolution during FreeSWITCH start-up. Options are 'nonat'
(default), 'nonatmap', 'autonat'.
</shortdesc>
<content type="string" default="${OCF_RESKEY_natdetect_default}" />
</parameter>

<parameter name="nocal" unique="0" required="0">
<longdesc lang="en">
During start-up, FreeSWITCH can run a series of tests which allow it to better
callibrate its internal timing systems. This could be useful if you are having issues
with RTP delivery timing. However, it can take another 5 - 10 seconds during start-up
to perform these tests. Because of the added start-up time, we recommend you disable
the callibration in an HA environment unless you find yourself experiencing issues
with timing of RTP delivery. 
</longdesc>
<shortdesc lang="en">
Disable timer callibration tests.
</shortdesc>
<content type="boolean" default="${OCF_RESKEY_nocal_default}" />
</parameter>

<parameter name="nort" unique="0" required="0">
<longdesc lang="en">
You probably do not want to change this unless you know that you are doing. Basically,
this disables the use of nanosleep calls inside FreeSWITCH and causes it to use
apr_sleep() instead. If you are not 100% certain what this will do to how FreeSWITCH
behaves, you should not change it.
</longdesc>
<shortdesc lang="en">
Disable use of clock_realtime. 
</shortdesc>
<content type="boolean" default="${OCF_RESKEY_nort_default}" />
</parameter>

<parameter name="coredir" unique="0" required="0">
<longdesc lang="en">
Specify the location where you want your core files to be dumped in the unlikely event
of a FreeSWITCH core.
</longdesc>
<shortdesc lang="en">
Specify the location where you want your core files to be dumped in the unlikely event
of a FreeSWITCH core.
</shortdesc>
<content type="string" default="${OCF_RESKEY_coredir_default}" />
</parameter>

<parameter name="confdir" unique="0" required="0">
<longdesc lang="en">
Specify alternate configuration directory.
</longdesc>
<shortdesc lang="en">
Specify alternate configuration directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_confdir_default}" />
</parameter>

<parameter name="logdir" unique="0" required="0">
<longdesc lang="en">
Specify alternate log directory.
</longdesc>
<shortdesc lang="en">
Specify alternate log directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_logdir_default}" />
</parameter>

<parameter name="dbdir" unique="0" required="0">
<longdesc lang="en">
Specify alternate db directory.
</longdesc>
<shortdesc lang="en">
Specify alternate db directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_dbdir_default}" />
</parameter>

<parameter name="rundir" unique="0" required="0">
<longdesc lang="en">
Specify alternate run directory.
</longdesc>
<shortdesc lang="en">
Specify alternate run directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_rundir_default}" />
</parameter>

<parameter name="moddir" unique="0" required="0">
<longdesc lang="en">
Specify alternate module directory.
</longdesc>
<shortdesc lang="en">
Specify alternate module directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_moddir_default}" />
</parameter>

<parameter name="htdocsdir" unique="0" required="0">
<longdesc lang="en">
Specify alternate htdocs directory.
</longdesc>
<shortdesc lang="en">
Specify alternate htdocs directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_htdocsdir_default}" />
</parameter>

<parameter name="scriptsdir" unique="0" required="0">
<longdesc lang="en">
Specify alternate scripts directory.
</longdesc>
<shortdesc lang="en">
Specify alternate scripts directory.
</shortdesc>
<content type="string" default="${OCF_RESKEY_scriptsdir_default}" />
</parameter>

</parameters>

<actions>
<action name="start" timeout="65"/>
<action name="stop"  timeout="60"/>
<action name="monitor" depth="0" timeout="5" interval="1" start-delay="65"/>
<action name="monitor" depth="0" timeout="5" interval="1" start-delay="65" role="Master"/>
<action name="meta-data" timeout="5"/>
<action name="validate-all" timeout="5"/>
<action name="promote" timeout="20"/>
<action name="demote" timeout="20"/>
</actions>
</resource-agent>
END

}

exec_cmd() {
	local cmd

	cmd=$1
	ocf_log debug "Executing: $cmd"
	$cmd &>/dev/null
	return $?
}

disable_selinux() {
	if [ ! -z "$(which setenforce)" ]
	then
		setenforce 0
	fi
	return $OCF_SUCCESS
}

enable_non_local_bind() {
	exec_cmd "echo 1 > /proc/sys/net/ipv4/ip_nonlocal_bind"
}

start() {
	local mac
	local rc
	local dbdir
	local startfs
	local fsextra

	disable_selinux
	enable_non_local_bind

	validate_all
	if [ $? -ne $OCF_SUCCESS ]
	then
		return $OCF_ERR_GENERIC
	fi

	exec_cmd "rm -f /var/run/freeswitch/{slave,master}"

	monitor_light
	rc=$?
	if [ $rc -eq $OCF_SUCCESS ] || [ $rc -eq $OCF_RUNNING_MASTER ]
	then
		ocf_log debug "FreeSWITCH is already running."
		return $OCF_SUCCESS
	fi
	ocf_log debug "Starting FreeSWITCH..."

	# Now we need to start FreeSWITCH
	mkdir -p /var/log/freeswitch
	HOST=$(hostname)
	PID=$$

	
	if [ ! -z "${OCF_RESKY_dbdir}" ]
	then
		dbdir=$OCF_RESKEY_dbdir
	else
		dbdir="${OCF_RESKEY_fsroot}/db"
	fi

	if [ ! -z "${OCF_RESKEY_confdir}" ]
	then
		fsextra="${fsextra} -conf ${OCF_RESKEY_confdir}"
	fi

	if [ ! -z "${OCF_RESKEY_logdir}" ]
	then
		fsextra="${fsextra} -log ${OCF_RESKEY_logdir}"
	fi

	if [ ! -z "${OCF_RESKEY_rundir}" ]
	then
		fsextra="${fsextra} -run ${OCF_RESKEY_rundir}"
	fi

	if [ ! -z "${OCF_RESKEY_dbdir}" ]
	then
		fsextra="${fsextra} -db ${OCF_RESKEY_dbdir}"
	fi

	if [ ! -z "${OCF_RESKEY_moddir}" ]
	then
		fsextra="${fsextra} -mod ${OCF_RESKEY_moddir}"
	fi

	if [ ! -z "${OCF_RESKEY_htdocsdir}" ]
	then
		fsextra="${fsextra} -htdocs ${OCF_RESKEY_htdocsdir}"
	fi

	if [ ! -z "${OCF_RESKEY_scriptsdir}" ]
	then
		fsextra="${fsextra} -scripts ${OCF_RESKEY_scriptsdir}"
	fi

	startfs="${OCF_RESKEY_fsroot}/bin/freeswitch -ncwait ${OCF_RESKEY_priority} ${OCF_RESKEY_natdetect} ${OCF_RESKEY_nocal} ${OCF_RESKEY_nort} ${fsextra}"
	
	exec_cmd "ulimit -c unlimited"
	exec_cmd "ulimit -d unlimited"
	exec_cmd "ulimit -f unlimited"
	exec_cmd "ulimit -i unlimited"
	exec_cmd "ulimit -n 999999"
	exec_cmd "ulimit -q unlimited"
	exec_cmd "ulimit -u unlimited"
	exec_cmd "ulimit -v unlimited"
	exec_cmd "ulimit -x unlimited"
	exec_cmd "ulimit -l unlimited"
	exec_cmd "ulimit -s 240"
	
	if [ -z "$(mount | grep ${dbdir})" ]
	then
		# dbdir is not mounted, so it is not an in-memory file system
		exec_cmd "rm -f ${dbdir}/*"
		exec_cmd "mount -t tmpfs none ${dbdir}"
	fi

	if [ ! -d "${OCF_RESKEY_coredir}" ]
	then
		mkdir -p ${OCF_RESKEY_coredir}
		if [ ! -d "${OCF_RESKEY_coredir}" ]
		then
			ocf_log err "Unable to create directory for storing core files."
			return $OCF_ERR_GENERIC
		fi
	fi

	exec_cmd "cd ${OCF_RESKEY_coredir}"
	exec_cmd "$startfs"

	if [ -z "$(pidof freeswitch | awk '{ORS=""; /(\d+)$/; print $1;}')" ]
	then
		return $OCF_ERR_GENERIC
	fi

	exec_cmd "touch /var/run/freeswitch/slave"
	if [ ! -f "/var/run/freeswitch/slave" ]
	then
		return $OCF_ERR_GENERIC
	fi
	exec_cmd "rm -f /var/run/freeswitch/master"

	monitor_light
	if [ $? -ne $OCF_SUCCESS ]
	then
		exec_cmd "rm -f /var/run/freeswitch/slave"
		return $OCF_ERR_GENERIC
	fi

	ocf_log debug "FreeSWITCH started successfully."
	return $OCF_SUCCESS
}

stop() {
	monitor_light
	if [ $? -eq $OCF_NOT_RUNNING ]
	then
		return $OCF_SUCCESS
	fi

	exec_cmd "rm -f /var/run/freeswitch/slave"
	if [ -f "/var/run/freeswitch/slave" ]
	then
		return $OCF_ERR_GENERIC
	fi

	${OCF_RESKEY_fs_cli_path} -x "fsctl pause"
	${OCF_RESKEY_fs_cli_path} -x "fsctl shutdown now"

	ocf_log debug "Waiting for freeswitch to terminate..."
	while [ ! -z "$(pidof freeswitch | awk '{ORS=""; /(\d+)$/; print $1;}')" ]
	do
		sleep 1
	done
	ocf_log debug "FreeSWITCH has stopped."

	reset_ips
	while [ $IPS ]; do
		ip_token ; get_dev ; get_ip ; get_class

		# Stop the IP address
		exec_cmd "ip addr del $IP_ADDR/$CLASS dev $DEV"
		if [ ! -z "$(ip addr list $DEV | grep $IP_ADDR)" ]
		then
			ocf_log err "Failed to stop $IP_ADDR/$CLASS on $DEV."
			return $OCF_ERR_GENERIC
		fi
	done

	monitor_light
	if [ $? -ne $OCF_NOT_RUNNING ]
	then
		return $OCF_ERR_GENERIC
	fi

	return $OCF_SUCCESS
}

reset_ips() {
	IPS=$OCF_RESKEY_ips
	TOKEN=''
	DEV=''
	IP_ADDR=''
	CLASS=''
}

ip_token() {
	TOKEN=${IPS%%:*}
	IPS=${IPS#*:}
	if [ "$TOKEN" == "$IPS" ]; then
		IPS=''
	fi
}

get_dev() {
	DEV=${TOKEN%%/*}
	TOKEN=${TOKEN#*/}
}

get_ip() {
	IP_ADDR=${TOKEN%%/*}
	TOKEN=${TOKEN#*/}
}

get_class() {
	CLASS=$TOKEN
}

monitor_light_ip() {
	if [ -z "$(ip addr list dev ${DEV} | grep ${IP_ADDR}/${CLASS})" ]
	then
		return $OCF_NOT_RUNNING
	fi
	return $OCF_SUCCESS
}

monitor_light_ips() {
	local rc

	RC_FIRST=-1
	reset_ips
	while [ $IPS ]; do
		ip_token ; get_dev ; get_ip ; get_class

		monitor_light_ip
		if [ $? -eq $OCF_NOT_RUNNING ]
		then
			return $OCF_NOT_RUNNING
		fi
	done
	return $OCF_SUCCESS
}

monitor_fs_light() {
	if [ ! -z "$(pidof freeswitch | awk '{ORS=""; /(\d+)$/; print $1;}')" ]; then
		return $OCF_SUCCESS
	fi
	return $OCF_NOT_RUNNING
}

monitor_fs_profiles() {
	local profs

	profs=$($OCF_RESKEY_fs_cli_path -x "sofia status" -t 100 2>/dev/null | grep RUNNING)
	if [ -z "$profs" ]
	then
		return $OCF_NOT_RUNNING
	fi

	reset_ips
	while [ $IPS ]; do
		ip_token ; get_dev ; get_ip ; get_class

		if [ -z "$(echo $profs | grep $IP_ADDR)" ]
		then
			return $OCF_NOT_RUNNING
		fi
	done
	return $OCF_SUCCESS
}

monitor_light() {
	local rc_ips
	local rc_fs
	local rc_profs

	monitor_light_ips
	rc_ips=$?
	if [ $rc_ips -eq $OCF_NOT_RUNNING ]
	then
		ocf_log debug "Monitor light on IPs returned NOT_RUNNING."
		if [ -f "/var/run/freeswitch/master" ]
		then
			ocf_log err "IP addresses are down but we are supposed to be master, monitor light returning FAILED_MASTER."
			return $OCF_FAILED_MASTER
		fi
	fi
	if [ $rc_ips -eq $OCF_SUCCESS ]
	then
		ocf_log debug "Monitor light on IPs returned SUCCESS."
		if [ ! -f "/var/run/freeswitch/master" ]
		then
			ocf_log err "IP addresses are up but we are supposed to be slave or stopped, monitor light returning ERR_GENERIC."
			return $OCF_ERR_GENERIC
		fi
	fi

	monitor_fs_light
	rc_fs=$?
	if [ $rc_fs -eq $OCF_NOT_RUNNING ]
	then
		ocf_log debug "Monitor light on FS returned NOT_RUNNING."
		if [ -f "/var/run/freeswitch/master" ]
		then
			ocf_log err "FreeSWITCH is not running but we are supposed to be master, monitor light returning FAILED_MASTER."
			return $OCF_FAILED_MASTER
		fi
		if [ -f "/var/run/freeswitch/slave" ]
		then
			ocf_log err "FreeSWITCH is not running but we are supposed to be slave, monitor light returning ERR_GENERIC."
			return $OCF_ERR_GENERIC
		fi
		ocf_log debug "We are supposed to be stopped, ignoring."
	fi
	if [ $rc_fs -eq $OCF_SUCCESS ]
	then
		ocf_log debug "Monitor light on FS returned SUCCESS."
		if [ ! -f "/var/run/freeswitch/master" ] && [ ! -f "/var/run/freeswitch/slave" ]
		then
			ocf_log err "FreeSWITCH is running but we are supposed to be stopped, monitor light returning ERR_GENERIC."
			return $OCF_ERR_GENERIC
		fi
		ocf_log debug "We are supposed to be either master or slave, ignoring."
	fi

	monitor_fs_profiles
	rc_profs=$?
	if [ $rc_profs -eq $OCF_NOT_RUNNING ]
	then
		ocf_log debug "Monitor light on FS profiles returned NOT_RUNNING."
		if [ -f "/var/run/freeswitch/master" ]
		then
			ocf_log err "Sofia profiles are down but we are supposed to be master, monitor light returning FAILED_MASTER."
			return $OCF_FAILED_MASTER
		fi
		if [ -f "/var/run/freeswitch/slave" ]
		then
			ocf_log err "Sofia profiles are down but we are supposed to be slave, monitor light returning ERR_GENERIC."
			return $OCF_ERR_GENERIC
		fi
		ocf_log debug "We are supposed to be stopped, ignoring."
	fi
	if [ $rc_profs -eq $OCF_SUCCESS ]
	then
		ocf_log debug "Monitor light on FS profiles returned SUCCESS."
		if [ ! -f "/var/run/freeswitch/master" ] && [ ! -f "/var/run/freeswitch/slave" ]
		then
			ocf_log err "Sofia profiles are running but we are supposed to be stopped, monitor light returning ERR_GENERIC."
			return $OCF_ERR_GENERIC
		fi
		ocf_log debug "We are supposed to be either master or slave, ignoring."
	fi

	if [ $rc_ips -eq $OCF_SUCCESS ] && [ $rc_fs -eq $OCF_SUCCESS ] && [ $rc_profs -eq $OCF_SUCCESS ]
	then
		ocf_log debug "IP addresses are up, FreeSWITCH is running, and Sofia profiles are up."
		if [ -f "/var/run/freeswitch/master" ]
		then
			ocf_log debug "Everything is up and we are supposed to be master, monitor light returning RUNNING_MASTER."
			return $OCF_RUNNING_MASTER
		fi
		ocf_log debug "We should not be seeing IP addresses up if we are not master, monitor light returning ERR_GENERIC."
		return $OCF_ERR_GENERIC
	fi
	if [ $rc_ips -eq $OCF_NOT_RUNNING ] && [ $rc_fs -eq $OCF_SUCCESS ] && [ $rc_profs -eq $OCF_SUCCESS ]
	then
		ocf_log debug "IP addresses are down, FreeSWITCH is running, and Sofia profiles are up."
		if [ -f "/var/run/freeswitch/slave" ]
		then
			ocf_log debug "Everything is up but IP addresses and we are supposed to be slave, monitor light returning SUCCESS."
			return $OCF_SUCCESS
		fi
		ocf_log debug "We should not be seeing this state if we are not slave, monitor light returning ERR_GENERIC."
		return $OCF_ERR_GENERIC
	fi
	if [ $rc_ips -eq $OCF_NOT_RUNNING ] && [ $rc_fs -eq $OCF_NOT_RUNNING ] && [ $rc_profs -eq $OCF_NOT_RUNNING ]
	then
		ocf_log debug "IP addresses are down, FreeSWITCH is stopped, and Sofia profiles are down."
		if [ ! -f "/var/run/freeswitch/master" ] && [ ! -f "/var/run/freeswitch/slave" ]
		then
			ocf_log debug "Everything is down and we are supposed to be stopped, monitor light returning SUCCESS."
			return $OCF_NOT_RUNNING
		fi
		ocf_log debug "We should not be seeing this state if we are not stopped, monitor light returning ERR_GENERIC."
		return $OCF_ERR_GENERIC
	fi

	ocf_log debug "Unkown state detected (IP=${rc_ips}; FS=${rc_fs}; PROF=${rc_profs}), monitor light returning ERR_GENERIC."
	ocf_log debug "/var/run/freeswitch: $(ls -al /var/run/freeswitch)"
	return $OCF_ERR_GENERIC
}

monitor_medium() {
	monitor_light
	return $?
}

monitor_heavy() {
	monitor_light
	return $?
}

monitor() {
	case $OCF_CHECK_LEVEL in
		0)
			monitor_light
			return $?
			;;
		10)
			monitor_medium
			return $?
			;;
		20)
			monitor_heavy
			return $?
			;;
	esac
}

validate_all() {
	local octet1
	local octet2
	local octet3
	local octet4
	local last3
	local last2

	if [ -z "$OCF_RESKEY_ips" ]
	then
		ocf_log err "FreeSWITCH requires you to specify the IP address information."
		return $OCF_ERR_CONFIGURED
	fi

	reset_ips
	while [ $IPS ]
	do
		ip_token ; get_dev ; get_ip ; get_class

		if [ -z "$DEV" ]
		then
			ocf_log err "Parse error while parsing 'ips' parameter."
			return $OCF_ERR_CONFIGURED
		fi

		if [ -z "$IP_ADDR" ]
		then
			ocf_log err "Parse error while parsing 'ips' parameter."
			return $OCF_ERR_CONFIGURED
		fi

		if [ -z "$CLASS" ]
		then
			ocf_log err "Parse error while parsing 'ips' parameter."
			return $OCF_ERR_CONFIGURED
		fi

		if [ -z "$(ip link list $DEV)" ]
		then
			ocf_log err "Device (${DEV}) does not exist."
			return $OCF_ERR_ARGS
		fi

		if ! ocf_is_decimal $CLASS
		then
			ocf_log err "The CIDR netmask ($CLASS) must be an integer between 4 and 32, inclusive."
			return $OCF_ERR_CONFIGURED
		fi

		if [ $CLASS -lt 4 ] || [ $CLASS -gt 32 ]
		then
			ocf_log err "The CIDR netmask ($CLASS) must be an integer between 4 and 32, inclusive."
			return $OCF_ERR_CONFIGURED
		fi

		octet1=${IP_ADDR%%.*}
		last3=${IP_ADDR#*.}
		octet2=${last3%%.*}
		last2=${last3#*.}
		octet3=${last2%%.*}
		octet4=${last2#*.}
		if ! ocf_is_decimal $octet1
		then
			ocf_log err "Octet 1 ($octet1) of your IP address ($IP_ADDR) is invalid."
			return $OCF_ERR_CONFIGURED
		fi

		if ! ocf_is_decimal $octet2
		then
			ocf_log err "Octet 2 ($octet2) of your IP address ($IP_ADDR) is invalid."
			return $OCF_ERR_CONFIGURED
		fi

		if ! ocf_is_decimal $octet3
		then
			ocf_log err "Octet 3 ($octet3) of your IP address ($IP_ADDR) is invalid."
			return $OCF_ERR_CONFIGURED
		fi

		if ! ocf_is_decimal $octet4
		then
			ocf_log err "Octet 4 ($octet4) of your IP address ($IP_ADDR) is invalid."
			return $OCF_ERR_CONFIGURED
		fi

		if [ $octet1 -lt 1 ] || [ $octet1 -gt 240 ]
		then
			ocf_log err "Octet 1 ($octet1) of you IP address ($IP_ADDR) must be between 1 and 240, inclusive."
			return $OCF_ERR_CONFIGURED
		fi

		if [ $octet2 -lt 0 ] || [ $octet2 -gt 255 ]
		then
			ocf_log err "Octet 2 ($octet2) of you IP address ($IP_ADDR) must be between 0 and 255, inclusive."
			return $OCF_ERR_CONFIGURED
		fi

		if [ $octet3 -lt 0 ] || [ $octet3 -gt 255 ]
		then
			ocf_log err "Octet 3 ($octet3) of you IP address ($IP_ADDR) must be between 0 and 255, inclusive."
			return $OCF_ERR_CONFIGURED
		fi

		if [ $octet4 -lt 0 ] || [ $octet4 -gt 255 ]
		then
			ocf_log err "Octet 4 ($octet4) of you IP address ($IP_ADDR) must be between 0 and 255, inclusive."
			return $OCF_ERR_CONFIGURED
		fi
	done

	case "$OCF_RESKEY_recover_calls" in
		1|true|TRUE|yes|YES|True|Yes) OCF_RESKEY_recover_calls=1;;
		0|false|FALSE|False|no|NO|No) OCF_RESKEY_recover_calls=0;;
		*) ocf_log err "The recover_calls parameter must be a boolean value which represents either true or false."
		   return $OCF_ERR_ARGS;;
	esac

	if [ ! -d "$OCF_RESKEY_fsroot" ]
	then
		ocf_log err "The fsroot parameter must signify an existing directory."
		return $OCF_ERR_ARGS
	fi

	if [ ! -x "${OCF_RESKEY_fsroot}/bin/freeswitch" ]
	then
		ocf_log err "The fsroot parameter does not point to a path where \${fsroot}/bin/freeswitch can be found."
		return $OCF_ERR_NOT_INSTALLED
	fi

	if [ ! -x "${OCF_RESKEY_fs_cli_path}" ]
	then
		ocf_log err "Unable to locate fs_cli. Check fs_cli_path: ${OCF_RESKEY_fs_cli_path}."
		return $OCF_ERR_NOT_INSTALLED
	fi

	if [ -z "$OCF_RESKEY_user" ]
	then
		if [ "$(id ${OCF_RESKEY_user} 2>&1)" == "id: ${OCF_RESKEY_user}: No such user" ]
		then
			ocf_log err "The specified user (${OCF_RESKEY_user}) does not exist."
			return $OCF_ERR_ARGS
		fi
	fi

	if [ -z "$OCF_RESKEY_group" ]
	then
		if [ -z "$(grep -P "^${OCF_RESKEY_group}:" /etc/group)" ]
		then
			ocf_log err "The specified group (${OCF_RESKEY_group}) does not exist."
			return $OCF_ERR_ARGS
		fi
	fi

	case "$OCF_RESKEY_priority" in
		realtime) OCF_RESKEY_priority='-rp';;
		      low) OCF_RESKEY_priority='-lp';;
		   normal) OCF_RESKEY_priority='-np';;
		        *) ocf_log err "Priority must be either 'realtime', 'low', or 'normal'."
		           return $OCF_ERR_ARGS;;
	esac

	case "$OCF_RESKEY_natdetect" in
		   nonat) OCF_RESKEY_natdetect='-nonat';;
		nonatmap) OCF_RESKEY_natdetect='-nonatmap';;
		 autonat) OCF_RESKEY_natdetect='';;
		       *) ocf_log err "The natdetect option must be either 'nonat', 'nonatmap', or 'autonat'."
			  return $OCF_ERR_ARGS;;
	esac

	case "$OCF_RESKEY_nocal" in
		1|true|TRUE|yes|YES|True|Yes) OCF_RESKEY_nocal="-nocal";;
		0|false|FALSE|False|no|NO|No) OCF_RESKEY_nocal="";;
		*) ocf_log err "The nocal parameter must be a boolean value which represents either true or false."
		   return $OCF_ERR_ARGS;;
	esac

	case "$OCF_RESKEY_nort" in
		1|true|TRUE|yes|YES|True|Yes) OCF_RESKEY_nort="-nort";;
		0|false|FALSE|False|no|NO|No) OCF_RESKEY_nort="";;
		*) ocf_log err "The nort parameter must be a boolean value which represents either true or false."
		   return $OCF_ERR_ARGS;;
	esac

	if [ -z "${OCF_RESKEY_coredir}" ]
	then
		ocf_log err "There must be a location for FreeSWITCH to dump core files."
		return $OCF_ERR_ARGS
	fi

	if [ ! -d "${OCF_RESKEY_coredir}" ]
	then
		exec_cmd "mkdir -p ${OCF_RESKEY_coredir}"
		if [ $? -ne $OCF_SUCCESS ]
		then
			ocf_log err "The core file directory does not exist, and there was an error attempting to create it: ${OCF_RESKEY_fs_cli_path}."
			return $OCF_ERR_ARGS
		fi
	fi

	if [ ! -z "${OCF_RESKEY_confdir}" ] && [ ! -d "${OCF_RESKEY_confdir}" ]
	then
		ocf_log err "An alternate confdir was specified ($OCF_RESKEY_confdir), but it does not exist."
		return $OCF_ERR_ARGS
	fi

	if [ ! -z "${OCF_RESKEY_logdir}" ] && [ ! -d "${OCF_RESKEY_logdir}" ]
	then
		ocf_log err "An alternate logdir was specified, but it does not exist."
		return $OCF_ERR_ARGS
	fi

	if [ ! -z "${OCF_RESKEY_moddir}" ] && [ ! -d "${OCF_RESKEY_moddir}" ]
	then
		ocf_log err "An alternate moddir was specified, but it does not exist."
		return $OCF_ERR_ARGS
	fi

	if [ ! -z "${OCF_RESKEY_dbdir}" ] && [ ! -d "${OCF_RESKEY_dbdir}" ]
	then
		ocf_log err "An alternate dbdir was specified, but it does not exist."
		return $OCF_ERR_ARGS
	fi

	if [ ! -z "${OCF_RESKEY_rundir}" ] && [ ! -d "${OCF_RESKEY_rundir}" ]
	then
		ocf_log err "An alternate rundir was specified, but it does not exist."
		return $OCF_ERR_ARGS
	fi

	if [ ! -z "${OCF_RESKEY_htdocsdir}" ] && [ ! -d "${OCF_RESKEY_htdocsdir}" ]
	then
		ocf_log err "An alternate htdocsdir was specified, but it does not exist."
		return $OCF_ERR_ARGS
	fi

	if [ ! -z "${OCF_RESKEY_scriptsdir}" ] && [ ! -d "${OCF_RESKEY_scriptsdir}" ]
	then
		ocf_log err "An alternate scriptsdir was specified, but it does not exist."
		return $OCF_ERR_ARGS
	fi

	return $OCF_SUCCESS
}

# We expect the IP addresses to control master/slave status.
promote() {
	local rc
	local result


	validate_all

	monitor_light
	rc=$?
	if [ $rc -eq $OCF_RUNNING_MASTER ]
	then
		return $OCF_SUCCESS
	fi
	if [ $rc -ne $OCF_SUCCESS ]
	then
		ocf_log err "FreeSWITCH is not running in a slave state; cannot promote to master. Returning ERR_GENERIC."
		return $OCF_ERR_GENERIC
	fi

	reset_ips
	while [ $IPS ]; do
		ip_token ; get_dev ; get_ip ; get_class

		# Start the IP address
		exec_cmd "ip addr add $IP_ADDR/$CLASS dev $DEV"
		if [ -z "$(ip addr list $DEV | grep $IP_ADDR)" ]
		then
			ocf_log err "Failed to start $IP_ADDR/$CLASS on $DEV."
			return $OCF_ERR_GENERIC
		fi

		if [ -x "/etc/sysconfig/network-scripts/ifup-routes" ]
		then
			exec_cmd "/etc/sysconfig/network-scripts/ifup-routes $DEV"
		fi

		$SENDARP -U -c 10 -I $DEV -s $IP_ADDR $IP_ADDR &
		if [ $? -ne 0 ]
		then
			ocf_log warn "Failed to start send_arp to broadcast gratuitous ARP messages for $IP_ADDR on $DEV."
		fi

	done

	if [ $OCF_RESKEY_recover_calls -ne 1 ]
	then
		ocf_log info "Attempting call recovery..."
		result=$($OCF_RESKEY_fs_cli_path -x "sofia recover" 2>/dev/null)
		if [ -z "$result" ]
		then
			ocf_log err "Failed to issue recovery command."
			return $OCF_ERR_GENERIC
		fi

		if [ "$result" == "No calls to recover." ]
		then
			ocf_log info "No calls were recovered. Sorry."
		else
			ocf_log info "Recovery command executed and returned: ${result}"
		fi
	fi

	exec_cmd "touch /var/run/freeswitch/master"
	if [ ! -f "/var/run/freeswitch/master" ]
	then
		ocf_log err "Failed to toggle into master mode."
		return $OCF_ERR_GENERIC
	fi

	exec_cmd "rm -f /var/run/freeswitch/slave"
	if [ -f "/var/run/freeswitch/slave" ]
	then
		ocf_log err "Failed to toggle out of slave mode."
		return $OCF_ERR_GENERIC
	fi

	monitor_light
	if [ $? -ne $OCF_RUNNING_MASTER ]
	then
		ocf_log err "Failed to promote FreeSWITCH to a master state. Returning ERR_GENERIC."
		return $OCF_ERR_GENERIC
	fi

	return $OCF_SUCCESS
}

# We expect the IP addresses to control master/slave status.
demote() {
	local rc

	monitor_light
	rc=$?
	if [ $rc -eq $OCF_SUCCESS ]
	then
		return $OCF_SUCCESS
	fi

	reset_ips
	while [ $IPS ]; do
		ip_token ; get_dev ; get_ip ; get_class

		# Stop the IP address
		exec_cmd "ip addr del $IP_ADDR/$CLASS dev $DEV"
		if [ ! -z "$(ip addr list $DEV | grep $IP_ADDR)" ]
		then
			ocf_log err "Failed to stop $IP_ADDR/$CLASS on $DEV."
			return $OCF_ERR_GENERIC
		fi
	done

	exec_cmd "touch /var/run/freeswitch/slave"
	if [ ! -f "/var/run/freeswitch/slave" ]
	then
		ocf_log err "Failed to toggle into slave mode."
		return $OCF_ERR_GENERIC
	fi

	exec_cmd "rm -f /var/run/freeswitch/master"
	if [ -f "/var/run/freeswitch/master" ]
	then
		ocf_log err "Failed to toggle out of master mode."
		return $OCF_ERR_GENERIC
	fi

	monitor_light
	if [ $? -ne $OCF_SUCCESS ]
	then
		return $OCF_ERR_GENERIC
	fi

	return $OCF_SUCCESS
}

notify() {
	return $OCF_SUCCESS
}

if [ $# -lt 1 ]; then
	usage
fi

OCF_TYPE=FreeSWITCH
OCF_RESOURCE_INSTANCE=${OCF_TYPE}
export OCF_TYPE OCF_RESOURCE_INSTANCE

IPS=$OCF_RESKEY_ips

case "$1" in
	'start')
		start
		;;
	'stop')
		stop
		;;
	'monitor')
		monitor
		;;
	'meta-data')
		meta_data
		;;
	'validate-all')
		validate_all
		;;
	'promote')
		promote
		;;
	'demote')
		demote
		;;
	'notify')
		notify
		;;
	*)
		;;
esac
exit $?

