#
#
# 	Common helper functions for the OCF Resource Agents supplied by
# 	heartbeat.
#
# Copyright (c) 2004 SUSE LINUX AG, Lars Marowsky-Brée
#                    All Rights Reserved.
#
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
# 

# Build version: 55a4e2c91cb846b2f35490645a1ebe832b7bdde9

# TODO: Some of this should probably split out into a generic OCF
# library for shell scripts, but for the time being, we'll just use it
# ourselves...
#

# TODO wish-list:
# - Generic function for evaluating version numbers
# - Generic function(s) to extract stuff from our own meta-data
# - Logging function which automatically adds resource identifier etc
#   prefixes
# TODO: Move more common functionality for OCF RAs here.
#

# This was common throughout all legacy Heartbeat agents
unset LC_ALL; export LC_ALL
unset LANGUAGE; export LANGUAGE

: ${HA_SBIN_DIR:=/usr/sbin}

__SCRIPT_NAME=`basename $0`

if [ -z "$OCF_ROOT" ]; then
    : ${OCF_ROOT=/usr/lib/ocf}
fi

if [ "$OCF_FUNCTIONS_DIR" = ${OCF_ROOT}/resource.d/heartbeat ]; then  # old
	unset OCF_FUNCTIONS_DIR
fi

: ${OCF_FUNCTIONS_DIR:=${OCF_ROOT}/lib/heartbeat}

. ${OCF_FUNCTIONS_DIR}/ocf-binaries
. ${OCF_FUNCTIONS_DIR}/ocf-returncodes
. ${OCF_FUNCTIONS_DIR}/ocf-directories
. ${OCF_FUNCTIONS_DIR}/ocf-rarun
. ${OCF_FUNCTIONS_DIR}/ocf-distro

# Define OCF_RESKEY_CRM_meta_interval in case it isn't already set,
# to make sure that ocf_is_probe() always works
: ${OCF_RESKEY_CRM_meta_interval=0}

ocf_is_root() {
	if [ X`id -u` = X0 ]; then
		true
	else
		false
	fi
}

ocf_maybe_random() {
	if test -c /dev/urandom; then
		od -An -N4 -tu4 /dev/urandom | tr -d '[:space:]'
	else
		awk -v pid=$$ 'BEGIN{srand(pid); print rand()}' | sed 's/^.*[.]//'
	fi
}

# Portability comments:
# o The following rely on Bourne "sh" pattern-matching, which is usually
#   that for filename generation (note: not regexp).
# o The "*) true ;;" clause is probably unnecessary, but is included
#   here for completeness.
# o The negation in the pattern uses "!".  This seems to be common
#   across many OSes (whereas the alternative "^" fails on some).
# o If an OS is encountered where this negation fails, then a possible
#   alternative would be to replace the function contents by (e.g.):
#	[ -z "`echo $1 | tr -d '[0-9]'`" ]
#
ocf_is_decimal() {
	case "$1" in
	""|*[!0-9]*)	# empty, or at least one non-decimal
		false ;;
	*)
		true ;;
	esac
}

ocf_is_true() {
	case "$1" in
	yes|true|1|YES|TRUE|True|ja|on|ON) true ;;
	*)	false ;;
	esac
}

ocf_is_hex() {
	case "$1" in
        ""|*[!0-9a-fA-F]*)	# empty, or at least one non-hex
		false ;;
	*)
		true ;;
	esac
}

ocf_is_octal() {
	case "$1" in
        ""|*[!0-7]*)	# empty, or at least one non-octal
		false ;;
	*)
		true ;;
	esac
}

__ocf_set_defaults() {
	__OCF_ACTION="$1"

	# Return to sanity for the agents...
	unset LANG
	LC_ALL=C
	export LC_ALL

	# TODO: Review whether we really should source this. Or rewrite
	# to match some emerging helper function syntax...? This imports
	# things which no OCF RA should be using...

	# Strip the OCF_RESKEY_ prefix from this particular parameter
	if [ -z "$OCF_RESKEY_OCF_CHECK_LEVEL" ]; then
		: ${OCF_CHECK_LEVEL:=0}
	else
		: ${OCF_CHECK_LEVEL:=$OCF_RESKEY_OCF_CHECK_LEVEL}
	fi

	if [ ! -d "$OCF_ROOT" ]; then
		ha_log "ERROR: OCF_ROOT points to non-directory $OCF_ROOT."
		exit $OCF_ERR_GENERIC
	fi

	if [ -z "$OCF_RESOURCE_TYPE" ]; then
		: ${OCF_RESOURCE_TYPE:=$__SCRIPT_NAME}
	fi

	if [ "x$__OCF_ACTION" = "xmeta-data" ]; then
		: ${OCF_RESOURCE_INSTANCE:="RESOURCE_ID"}
	fi

	if [ -z "$OCF_RA_VERSION_MAJOR" ]; then
		: We are being invoked as an init script.
		: Fill in some things with reasonable values.
		: ${OCF_RESOURCE_INSTANCE:="default"}
		return 0
        fi

	if [ -z "$OCF_RESOURCE_INSTANCE" ]; then
		ha_log "ERROR: Need to tell us our resource instance name."
		exit $OCF_ERR_ARGS
	fi
}

hadate() {
  date "+${HA_DATEFMT}"
}

set_logtag() {
	if [ -z "$HA_LOGTAG" ]; then
		if [ -n "$OCF_RESOURCE_INSTANCE" ]; then
			HA_LOGTAG="$__SCRIPT_NAME($OCF_RESOURCE_INSTANCE)[$$]"
		else
			HA_LOGTAG="$__SCRIPT_NAME[$$]"
		fi
	fi
}

__ha_log() {
	local ignore_stderr=false
	local loglevel

	[ "x$1" = "x--ignore-stderr" ] && ignore_stderr=true && shift

	[ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""
	# if we're connected to a tty, then output to stderr
	if tty >/dev/null; then
		if [ "x$HA_debug" = "x0" -a "x$loglevel" = xdebug ] ; then
			return 0
		elif [ "$ignore_stderr" = "true" ]; then
			# something already printed this error to stderr, so ignore
			return 0
		fi
		if [ "$HA_LOGTAG" ]; then
			echo "$HA_LOGTAG: $*"
		else
			echo "$*"
		fi >&2
		return 0
	fi

	set_logtag

	if [ "x${HA_LOGD}" = "xyes" ] ; then 
		ha_logger -t "${HA_LOGTAG}" "$@"
		if [ "$?" -eq "0" ] ; then
			return 0
		fi
	fi

	if
	  [ -n "$HA_LOGFACILITY" ]
        then
	  : logging through syslog
	  # loglevel is unknown, use 'notice' for now
          loglevel=notice
          case "${*}" in
            *ERROR*)		loglevel=err;;
            *WARN*)		loglevel=warning;;
            *INFO*|info)	loglevel=info;;
	  esac
	  logger -t "$HA_LOGTAG" -p ${HA_LOGFACILITY}.${loglevel} "${*}"
        fi	
        if
	  [ -n "$HA_LOGFILE" ]
	then
	  : appending to $HA_LOGFILE
	  echo `hadate`" $HA_LOGTAG:    ${*}" >> $HA_LOGFILE
	fi
	if
	  [ -z "$HA_LOGFACILITY" -a -z "$HA_LOGFILE" ] && ! [ "$ignore_stderr" = "true" ]
	then
	  : appending to stderr
	  echo `hadate`"${*}" >&2
	fi
        if
          [ -n "$HA_DEBUGLOG" ]
        then
          : appending to $HA_DEBUGLOG
		  if [ "$HA_LOGFILE"x != "$HA_DEBUGLOG"x ]; then
            echo "$HA_LOGTAG:	"`hadate`"${*}" >> $HA_DEBUGLOG
          fi
        fi
}

ha_log()
{
	__ha_log "$@"
}

ha_debug() {

        if [ "x${HA_debug}" = "x0" ] || [ -z "${HA_debug}" ] ; then
                return 0
        fi
	if tty >/dev/null; then
		if [ "$HA_LOGTAG" ]; then
			echo "$HA_LOGTAG: $*"
		else
			echo "$*"
		fi >&2
		return 0
	fi

	set_logtag

        if [ "x${HA_LOGD}" = "xyes" ] ; then  
		ha_logger -t "${HA_LOGTAG}" -D "ha-debug" "$@"
                if [ "$?" -eq "0" ] ; then
                        return 0
                fi
        fi

	[ none = "$HA_LOGFACILITY" ] && HA_LOGFACILITY=""

	if
	  [ -n "$HA_LOGFACILITY" ]
	then
	  : logging through syslog
	  logger -t "$HA_LOGTAG" -p "${HA_LOGFACILITY}.debug" "${*}"
	fi
        if
	  [ -n "$HA_DEBUGLOG" ]
	then
	  : appending to $HA_DEBUGLOG
	  echo "$HA_LOGTAG:	"`hadate`"${*}" >> $HA_DEBUGLOG
	fi
	if
	  [ -z "$HA_LOGFACILITY" -a -z "$HA_DEBUGLOG" ]
	then
	  : appending to stderr
	  echo "$HA_LOGTAG:	`hadate`${*}:	${HA_LOGFACILITY}" >&2
	fi
}

ha_parameter() {
	local VALUE
    VALUE=`sed -e 's%[	][	]*% %' -e 's%^ %%' -e 's%#.*%%'   $HA_CF | grep -i "^$1 " | sed 's%[^ ]* %%'`
    if
	[ "X$VALUE" = X ]
    then
	
	case $1 in
	    keepalive)	VALUE=2;;
	    deadtime)
		ka=`ha_parameter keepalive`
		VALUE=`expr $ka '*' 2 '+' 1`;;
	esac
    fi
    echo $VALUE
}

ocf_log() {
	# TODO: Revisit and implement internally.
	if
          [ $# -lt 2 ]
        then
          ocf_log err "Not enough arguments [$#] to ocf_log."
        fi
        __OCF_PRIO="$1"
        shift
        __OCF_MSG="$*"

        case "${__OCF_PRIO}" in
          crit)	__OCF_PRIO="CRIT";;
          err)	__OCF_PRIO="ERROR";;
          warn)	__OCF_PRIO="WARNING";;
          info)	__OCF_PRIO="INFO";;
          debug)__OCF_PRIO="DEBUG";;
          *)	__OCF_PRIO=`echo ${__OCF_PRIO}| tr '[a-z]' '[A-Z]'`;;
	esac

	if [ "${__OCF_PRIO}" = "DEBUG" ]; then
		ha_debug "${__OCF_PRIO}: $__OCF_MSG"
	else
		ha_log "${__OCF_PRIO}: $__OCF_MSG"
	fi
}

#
# ocf_exit_reason: print exit error string to stderr
# Usage:           Allows the OCF script to provide a string
#                  describing why the exit code was returned.
# Arguments:   reason - required, The string that represents why the error
#                       occured.
#
ocf_exit_reason()
{
	local cookie="$OCF_EXIT_REASON_PREFIX"
	local fmt
	local msg

	# No argument is likely not intentional.
	# Just one argument implies a printf format string of just "%s".
	# "Least surprise" in case some interpolated string from variable
	# expansion or other contains a percent sign.
	# More than one argument: first argument is going to be the format string.
	case $# in
	0)	ocf_log err "Not enough arguments to ocf_log_exit_msg." ;;
	1)	fmt="%s" ;;

	*)	fmt=$1
		shift
		case $fmt in
		*%*) : ;; # ok, does look like a format string
		*) ocf_log warn "Does not look like format string: [$fmt]" ;;
		esac ;;
	esac

	if [ -z "$cookie" ]; then
		# use a default prefix
		cookie="ocf-exit-reason:"
	fi

	msg=$(printf "${fmt}" "$@")
	printf >&2 "%s%s\n" "$cookie" "$msg"
	__ha_log --ignore-stderr "ERROR: $msg"
}

#
# ocf_deprecated: Log a deprecation warning
# Usage:          ocf_deprecated [param-name]
# Arguments:      param-name optional, name of a boolean resource
#                            parameter that can be used to suppress
#                            the warning (default
#                            "ignore_deprecation")
ocf_deprecated() {
    local param
    param=${1:-ignore_deprecation}
    # don't use ${!param} here, it's a bashism
    if ! ocf_is_true $(eval echo \$OCF_RESKEY_$param); then
	ocf_log warn "This resource agent is deprecated" \
	    "and may be removed in a future release." \
	    "See the man page for details." \
	    "To suppress this warning, set the \"${param}\"" \
	    "resource parameter to true."
    fi
}

#
# Ocf_run: Run a script, and log its output.
# Usage:   ocf_run [-q] [-info|-warn|-err] <command>
#	-q: don't log the output of the command if it succeeds
#	-info|-warn|-err: log the output of the command at given
#		severity if it fails (defaults to err)
#
ocf_run() {
	local rc
	local output
	local verbose=1
	local loglevel=err
	local var

	for var in 1 2
	do
	    case "$1" in
		"-q")
		    verbose=""
		    shift 1;;
		"-info"|"-warn"|"-err")
		    loglevel=`echo $1 | sed -e s/-//g`
		    shift 1;;
		*)
		    ;;		
	    esac
	done

	output=`"$@" 2>&1`
	rc=$?
	[ -n "$output" ] && output="$(echo "$output" | tr -s ' \t\r\n' ' ')"
	if [ $rc -eq 0 ]; then 
	    if [ "$verbose" -a ! -z "$output" ]; then
		ocf_log info "$output"
	    fi
	else
	    if [ ! -z "$output" ]; then
		ocf_log $loglevel "$output"
	    else
		ocf_log $loglevel "command failed: $*"
	    fi
	fi

	return $rc
}

ocf_pidfile_status() {
    local pid pidfile=$1
    if [ ! -e $pidfile ]; then
	# Not exists
	return 2
    fi
    pid=`cat $pidfile`
    kill -0 $pid > /dev/null 2>&1
    if [ $? = 0 ]; then
	return 0
    fi

    # Stale
    return 1
}

# mkdir(1) based locking
# first the directory is created with the name given as $1
# then a file named "pid" is created within that directory with
# the process PID
# stale locks are handled carefully, the inode of a directory
# needs to match before and after test if the process is running
# empty directories are also handled appropriately
# we relax (sleep) occasionally to allow for other processes to
# finish managing the lock in case they are in the middle of the
# business

relax() { sleep 0.5; }
ocf_get_stale_pid() {
	local piddir pid dir_inode

	piddir="$1"
	[ -z "$piddir" ] && return 2
	dir_inode="`ls -di $piddir 2>/dev/null`"
	[ -z "$dir_inode" ] && return 1
	pid=`cat $piddir/pid 2>/dev/null`
	if [ -z "$pid" ]; then
		# empty directory?
		relax
		if [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
			echo $dir_inode
		else
			return 1
		fi
	elif kill -0 $pid >/dev/null 2>&1; then
		return 1
	elif relax && [ -e "$piddir/pid" ] && [ "$dir_inode" = "`ls -di $piddir 2>/dev/null`" ]; then
		echo $pid
	else
		return 1
	fi
}

# There is a race when the following two functions to manage the
# lock file (mk and rm) are invoked in parallel by different
# instances. It is up to the caller to reduce probability of that
# taking place (see ocf_take_lock() below).

ocf_mk_pid() {
	mkdir $1 2>/dev/null && echo $$ > $1/pid
}
ocf_rm_pid() {
	rm -f $1/pid
	rmdir $1 2>/dev/null
}

# Testing and subsequently removing a stale lock (containing the
# process pid) is inherently difficult to do in such a way as to
# prevent a race between creating a pid file and removing it and
# its directory. We reduce the probability of that happening by
# checking if the stale lock persists over a random period of
# time.

ocf_take_lock() {
	local lockdir=$1
	local rnd
	local stale_pid

	# we don't want it too short, so strip leading zeros
	rnd=$(ocf_maybe_random | sed 's/^0*//')
	stale_pid=`ocf_get_stale_pid $lockdir`
	if [ -n "$stale_pid" ]; then
		sleep 0.$rnd
		# remove "stale pid" only if it persists
		[ "$stale_pid" = "`ocf_get_stale_pid $lockdir`" ] &&
			ocf_rm_pid $lockdir
	fi
	while ! ocf_mk_pid $lockdir; do
		ocf_log info "Sleeping until $lockdir is released..."
		sleep 0.$rnd
	done
}

ocf_release_lock_on_exit() {
	trap "ocf_rm_pid $1" EXIT
}

# returns true if the CRM is currently running a probe. A probe is
# defined as a monitor operation with a monitoring interval of zero.
ocf_is_probe() {
    [ "$__OCF_ACTION" = "monitor" -a "$OCF_RESKEY_CRM_meta_interval" = 0 ]
}

# returns true if the resource is configured as a clone. This is
# defined as a resource where the clone-max meta attribute is present.
ocf_is_clone() {
    [ ! -z "${OCF_RESKEY_CRM_meta_clone_max}" ]
}

# returns true if the resource is configured as a multistate
# (master/slave) resource. This is defined as a resource where the
# master-max meta attribute is present, and set to greater than zero.
ocf_is_ms() {
    [ "${OCF_RESKEY_CRM_meta_promotable}" = "true" ] || { [ ! -z "${OCF_RESKEY_CRM_meta_master_max}" ] && [ "${OCF_RESKEY_CRM_meta_master_max}" -gt 0 ]; }
}

# version check functions
# allow . and - to delimit version numbers
# max version number is 999
#
ocf_is_ver() {
	echo $1 | grep '^[0-9][0-9.-]*[0-9A-Za-z.\+-]*$' >/dev/null 2>&1
}

# usage: ocf_version_cmp VER1 VER2
#     version strings can contain digits, dots, and dashes
#     must start and end with a digit
# returns:
#     0: VER1 smaller (older) than VER2
#     1: versions equal
#     2: VER1 greater (newer) than VER2
#     3: bad format
ocf_version_cmp() {
	ocf_is_ver "$1" || return 3
	ocf_is_ver "$2" || return 3
	local v1=$1
	local v2=$2

	sort_version="sort -t. -k 1,1n -k 2,2n -k 3,3n -k 4,4n"
	older=$( (echo "$v1"; echo "$v2") | $sort_version | head -1 )

	if [ "$v1" = "$v2" ]; then
		return 1
	elif [ "$v1" = "$older" ]; then
		return 0
	else
		return 2 # -1 would look funny in shell ;-)
	fi
}

ocf_local_nodename() {
	# use crm_node -n for pacemaker > 1.1.8
	which pacemakerd > /dev/null 2>&1
	if [ $? -eq 0 ]; then
		local version=$(pacemakerd -$ | grep "Pacemaker .*" | awk '{ print $2 }')
		version=$(echo $version | awk -F- '{ print $1 }')
		ocf_version_cmp "$version" "1.1.8"
		if [ $? -eq 2 ]; then
			which crm_node > /dev/null 2>&1
			if [ $? -eq 0 ]; then
				crm_node -n
				return
			fi
		fi
	fi

	# otherwise use uname -n
	uname -n
}

# usage: dirname DIR
dirname()
{
	local a
	local b

	[ $# = 1 ] || return 1
	a="$1"
	while [ 1 ]; do
		b="${a%/}"
		[ "$a" = "$b" ] && break
		a="$b"
	done
	b=${a%/*}
	[ -z "$b" -o "$a" = "$b"  ] && b="."

	echo "$b"
	return 0
}

# usage: systemd_is_running
# returns:
#    0  PID 1 is systemd
#    1  otherwise
systemd_is_running()
{
	[ "$(cat /proc/1/comm 2>/dev/null)" = "systemd" ]
}

# usage: systemd_drop_in <name> <After|Before> <dependency.service>
systemd_drop_in()
{
	local conf_file
	if [ $# -ne 3 ]; then
          ocf_log err "Incorrect number of arguments [$#] for systemd_drop_in."
        fi

	systemdrundir="/run/systemd/system/resource-agents-deps.target.d"
	mkdir -p "$systemdrundir"
	conf_file="$systemdrundir/$1.conf"
	cat >"$conf_file" <<EOF
[Unit]
$2=$3
EOF
	# The information is accessible through systemd API and systemd would
	# complain about improper permissions.
	chmod o+r "$conf_file"
	systemctl daemon-reload
}

# move process to root cgroup if realtime scheduling is enabled
ocf_move_to_root_cgroup_if_rt_enabled()
{
	if [ -e "/sys/fs/cgroup/cpu/cpu.rt_runtime_us" ]; then
		echo $$ >> /sys/fs/cgroup/cpu/tasks

		if [ "$?" -ne "0" ]; then
			ocf_log warn "Unable to move PID $$ to the root cgroup"
		fi
	fi
}

# usage: curl_retry RETRIES SLEEP ARGS URL
#
# Use --show-error in ARGS to log HTTP error code
#
# returns:
#    0  success
# exit:
#    1  fail
curl_retry()
{
	local retries=$1 sleep=$2 opts=$3 url=$4
	local tries=$(($retries + 1))
	local args="--fail $opts $url"
	local result rc

	for try in $(seq $tries); do
		ocf_log debug "curl $args try $try of $tries"
		result=$(echo "$args" | xargs curl 2>&1)
		rc=$?

		ocf_log debug "result: $result"
		[ $rc -eq 0 ] && break
		if [ -n "$TOKEN" ] && [ -n "$TOKEN_FILE" ] && \
		   [ -f "$TOKEN_FILE" ] && [ -n "$TOKEN_FUNC" ] && \
		   echo "$result" | grep -q "The requested URL returned error: 401$"; then
			local OLD_TOKEN="$TOKEN"
			ocf_log err "Token invalid. Getting new token."
			TOKEN=$($TOKEN_FUNC)
			[ $? -ne 0 ] && exit $OCF_ERR_GENERIC
			args=$(echo "$args" | sed "s/$OLD_TOKEN/$TOKEN/")
		fi
		sleep $sleep
	done

	if [ $rc -ne 0 ]; then
		ocf_exit_reason "curl $args failed $tries tries"
		exit $OCF_ERR_GENERIC
	fi

	echo "$result"
	return $rc
}

# usage: crm_mon_no_validation args...
# run crm_mon without any cib schema validation
# This is useful when an agent runs in a bundle to avoid potential
# schema validation errors when host and bundle are not perfectly aligned
# To be used, your shell must support on process substitution (e.g. bash)
# returns:
#    <crm_mon error codes>
crm_mon_no_validation()
{
	# The subshell prevents parsing error with incompatible shells
	"$SHELL" -c "CIB_file=<(${HA_SBIN_DIR}/cibadmin -Q | sed 's/validate-with=\"[^\"]*\"/validate-with=\"none\"/') \
	${HA_SBIN_DIR}/crm_mon \$*" -- $*
}

#
# pseudo_resource status tracking function...
#
# This allows pseudo resources to give correct status information.  As we add
# resource monitoring, and better resource tracking in general, this will
# become essential.
#
# These scripts work because ${HA_RSCTMP} is cleaned on node reboot.
#
# We create "resource-string" tracking files under ${HA_RSCTMP} in a
# very simple way:
#
#	Existence of "${HA_RSCTMP}/resource-string" means that we consider
#	the resource named by "resource-string" to be running.
#
# Note that "resource-string" needs to be unique.  Using the resource type
# plus the resource instance arguments to make up the resource string
# is probably sufficient...
#
# usage: ha_pseudo_resource resource-string op [tracking_file]
# 	where op is {start|stop|monitor|status|restart|reload|print}
#	print is a special op which just prints the tracking file location
#	user can override our choice of the tracking file location by
#		specifying it as the third arg
#	Note that all operations are silent...
#
ha_pseudo_resource()
{
  local ha_resource_tracking_file="${3:-${HA_RSCTMP}/$1}"
  case $2 in
    start|restart|reload)  touch "$ha_resource_tracking_file";;
    stop) rm -f "$ha_resource_tracking_file";;
    status|monitor)
           if
             [ -f "$ha_resource_tracking_file" ]
           then
             return 0
           else
             case $2 in
               status)	return 3;;
               *)	return 7;;
             esac
           fi;;
    print)  echo "$ha_resource_tracking_file";;
    *)	return 3;;
  esac
}

# usage: rmtempdir TMPDIR
rmtempdir()
{
	[ $# = 1 ] || return 1
	if [ -e "$1" ]; then
		rmdir "$1" || return 1
	fi
	return 0
}

# usage: maketempfile [-d]
maketempfile()
{
	if [ $# = 1 -a "$1" = "-d" ]; then
		mktemp -d
		return 0
	elif [ $# != 0 ]; then
		return 1
	fi

	mktemp
	return 0
}

# usage: rmtempfile TMPFILE
rmtempfile ()
{
	[ $# = 1 ] || return 1
	if [ -e "$1" ]; then
		rm "$1" || return 1
	fi
	return 0
}

# echo the first lower supported check level
# pass set of levels supported by the agent
# (in increasing order, 0 is optional)
ocf_check_level()
{
	local lvl prev
	lvl=0
	prev=0
	if ocf_is_decimal "$OCF_CHECK_LEVEL"; then
		# the level list should be very short
		for lvl; do
			if [ "$lvl" -eq "$OCF_CHECK_LEVEL" ]; then
				break
			elif [ "$lvl" -gt "$OCF_CHECK_LEVEL" ]; then
				lvl=$prev # the previous one
				break
			fi
			prev=$lvl
		done
	fi
	echo $lvl
}

# usage: ocf_stop_processes SIGNALS WAIT_TIME PIDS
#
# we send signals (use quotes for more than one!) in the order
# given; if one or more processes are still running we try KILL;
# the wait_time is the _total_ time we'll spend in this function
# this time may be slightly exceeded if the processes won't leave
# 
# returns:
#     0: all processes left
#     1: some processes still running
#
# example:
#
# ocf_stop_processes TERM 5 $pids
# 
ocf_stop_processes() {
	local signals="$1"
	local wait_time="$(($2/`echo $signals|wc -w`))"
	shift 2
	local pids="$*"
	local sig i
	test -z "$pids" &&
		return 0
	for sig in $signals KILL; do
		kill -s $sig $pids 2>/dev/null
		# try to leave early, and yet leave processes time to exit
		sleep 0.2
		for i in `seq $wait_time`; do
			kill -s 0 $pids 2>/dev/null ||
				return 0
			sleep 1
		done
	done
	return 1
}

#
# create a given status directory
# if the directory path doesn't start with $HA_VARRUN, then
# we return with error (most of the calls would be with the user
# supplied configuration, hence we need to do necessary
# protection)
# used mostly for PID files
#
# usage: ocf_mkstatedir owner permissions path
#
# owner: user.group
# permissions: permissions
# path: directory path
#
# example:
#	ocf_mkstatedir named 755 `dirname $pidfile`
#
ocf_mkstatedir()
{
	local owner
	local perms
	local path

	owner=$1
	perms=$2
	path=$3

	test -d $path && return 0
	[ $(id -u) = 0 ] || return 1

	case $path in
	${HA_VARRUN%/}/*) : this path is ok ;;
	*) ocf_log err "cannot create $path (does not start with $HA_VARRUN)"
		return 1
	;;
	esac

	mkdir -p $path &&
	chown $owner $path &&
	chmod $perms $path
}

#
# create a unique status directory in $HA_VARRUN
# used mostly for PID files
# the directory is by default set to
#   $HA_VARRUN/$OCF_RESOURCE_INSTANCE
# the directory name is printed to stdout
#
# usage: ocf_unique_rundir owner permissions name
#
# owner: user.group (default: "root")
# permissions: permissions (default: "755")
# name: some unique string (default: "$OCF_RESOURCE_INSTANCE")
#
# to use the default either don't set the parameter or set it to
# empty string ("")
# example:
#
#	STATEDIR=`ocf_unique_rundir named "" myownstatedir`
#
ocf_unique_rundir()
{
	local path
	local owner
	local perms
	local name

	owner=${1:-"root"}
	perms=${2:-"755"}
	name=${3:-"$OCF_RESOURCE_INSTANCE"}
	path=$HA_VARRUN/$name
	if [ ! -d $path ]; then
		[ $(id -u) = 0 ] || return 1
		mkdir -p $path &&
		chown $owner $path &&
		chmod $perms $path || return 1
	fi
	echo $path
}

#
# RA tracing may be turned on by setting OCF_TRACE_RA
# the trace output will be saved to OCF_TRACE_FILE, if set, or
# by default to
#   $HA_VARLIB/trace_ra/<type>/<id>.<action>.<timestamp>
#   e.g. $HA_VARLIB/trace_ra/oracle/db.start.2012-11-27.08:37:08
#
# OCF_TRACE_FILE:
# - FD (small integer [3-9]) in that case it is up to the callers
#   to capture output; the FD _must_ be open for writing
# - absolute path
#
# NB: FD 9 may be used for tracing with bash >= v4 in case
# OCF_TRACE_FILE is set to a path.
#
ocf_bash_has_xtracefd() {
	[ -n "$BASH_VERSION" ] && [ ${BASH_VERSINFO[0]} -ge 4 ]
}
# for backwards compatibility
ocf_is_bash4() {
	ocf_bash_has_xtracefd
}
ocf_trace_redirect_to_file() {
	local dest=$1
	if ocf_bash_has_xtracefd; then
		exec 9>$dest
		BASH_XTRACEFD=9
	else
		exec 2>$dest
	fi
}
ocf_trace_redirect_to_fd() {
	local fd=$1
	if ocf_bash_has_xtracefd; then
		BASH_XTRACEFD=$fd
	else
		exec 2>&$fd
	fi
}
__ocf_test_trc_dest() {
	local dest=$1
	if ! touch $dest; then
		ocf_log warn "$dest not writable, trace not going to happen"
		__OCF_TRC_DEST=""
		__OCF_TRC_MANAGE=""
		return 1
	fi
	return 0
}
ocf_default_trace_dest() {
	tty >/dev/null && return
	if [ -n "$OCF_RESOURCE_TYPE" -a \
			-n "$OCF_RESOURCE_INSTANCE" -a -n "$__OCF_ACTION" ]; then
		local ts=`date +%F.%T`
		__OCF_TRC_DEST=$HA_VARLIB/trace_ra/${OCF_RESOURCE_TYPE}/${OCF_RESOURCE_INSTANCE}.${__OCF_ACTION}.$ts
		__OCF_TRC_MANAGE="1"
	fi
}

ocf_start_trace() {
	export __OCF_TRC_DEST="" __OCF_TRC_MANAGE=""
	case "$OCF_TRACE_FILE" in
	[3-9]) ocf_trace_redirect_to_fd "$OCF_TRACE_FILE" ;;
	/*/*) __OCF_TRC_DEST=$OCF_TRACE_FILE ;;
	"") ocf_default_trace_dest ;;
	*)
		ocf_log warn "OCF_TRACE_FILE must be set to either FD (open for writing) or absolute file path"
		ocf_default_trace_dest
		;;
	esac
	if [ "$__OCF_TRC_DEST" ]; then
		mkdir -p `dirname $__OCF_TRC_DEST`
		__ocf_test_trc_dest $__OCF_TRC_DEST ||
			return
		ocf_trace_redirect_to_file "$__OCF_TRC_DEST"
	fi
	if [ -n "$BASH_VERSION" ]; then
		PS4='+ `date +"%T"`: ${FUNCNAME[0]:+${FUNCNAME[0]}:}${LINENO}: '
	fi
	set -x
	env=$( echo; printenv | sort )
}
ocf_stop_trace() {
	set +x
}

# Helper functions to map from nodename/bundle-name and physical hostname
# list_index_for_word "node0 node1 node2 node3 node4 node5" node4 --> 5
# list_word_at_index "NA host1 host2 host3 host4 host5" 3      --> host2

# list_index_for_word "node1 node2 node3 node4 node5" node7 --> ""
# list_word_at_index "host1 host2 host3 host4 host5" 8      --> ""

# attribute_target node1                                    --> host1
list_index_for_word() {
	echo $1 | tr ' ' '\n' | awk -v x="$2" '$0~x {print NR}'
}

list_word_at_index() {
	echo $1 | tr ' ' '\n' | awk -v n="$2" 'n == NR'
}

ocf_attribute_target() {
	if [ x$1 = x ]; then
		if [ x$OCF_RESKEY_CRM_meta_container_attribute_target = xhost -a x$OCF_RESKEY_CRM_meta_physical_host != x ]; then
			echo $OCF_RESKEY_CRM_meta_physical_host
		else
			if [ x$OCF_RESKEY_CRM_meta_on_node != x ]; then
				echo $OCF_RESKEY_CRM_meta_on_node
			else
				ocf_local_nodename
			fi
		fi
		return
	elif [ x"$OCF_RESKEY_CRM_meta_notify_all_uname" != x ]; then
		index=$(list_index_for_word "$OCF_RESKEY_CRM_meta_notify_all_uname" $1)
		mapping=""
		if [ x$index != x ]; then
			mapping=$(list_word_at_index "$OCF_RESKEY_CRM_meta_notify_all_hosts" $index)
		fi
		if [ x$mapping != x -a x$mapping != xNA ]; then
			echo $mapping
			return
		fi
	fi
	echo $1
}

__ocf_set_defaults "$@"

: ${OCF_TRACE_RA:=$OCF_RESKEY_trace_ra}
ocf_is_true "$OCF_TRACE_RA" && ocf_start_trace

# pacemaker sets HA_use_logd, some others use HA_LOGD :/
if ocf_is_true "$HA_use_logd"; then
	: ${HA_LOGD:=yes}
fi
