#!/bin/sh
# exportfs
#
# Description: Manages nfs exported file system.
#
#   (c) 2010 Ben Timby, Florian Haas, Dejan Muhamedagic,
#            and Linux-HA contributors
#
# License: GNU General Public License v2 (GPLv2) and later

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

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

<longdesc lang="en">
Exportfs uses the exportfs command to add/remove nfs exports.
It does NOT manage the nfs server daemon.
It depends on Linux specific NFS implementation details,
so is considered not portable to other platforms yet.
</longdesc>

<shortdesc lang="en">
Manages NFS exports
</shortdesc>

<parameters>

<parameter name="clientspec" unique="0" required="1">
<longdesc lang="en">
The client specification allowing remote machines to mount the directory
over NFS.
</longdesc>
<shortdesc lang="en">
Client ACL.
</shortdesc>
<content type="string" />
</parameter>

<parameter name="options" unique="0" required="0">
<longdesc lang="en">
The options to pass to exportfs for the exported directory.
</longdesc>
<shortdesc lang="en">
Export options.
</shortdesc>
<content type="string" />
</parameter>

<parameter name="directory" unique="0" required="1">
<longdesc lang="en">
The directory which you wish to export using NFS.
</longdesc>
<shortdesc lang="en">
The directory to export.
</shortdesc>
<content type="string" />
</parameter>

<parameter name="fsid" unique="1" required="1">
<longdesc lang="en">
The fsid option to pass to exportfs. This should be a unique positive integer,
avoid 0 unless you understand its special status.
This value will override any fsid provided via the options parameter.
</longdesc>
<shortdesc lang="en">
Unique fsid within cluster.
</shortdesc>
<content type="integer" />
</parameter>

</parameters>

<actions>
<action name="start"   timeout="40" />
<action name="stop"    timeout="10" />
<action name="monitor" depth="0"  timeout="20" interval="10" />
<action name="meta-data"  timeout="5" />
<action name="validate-all"  timeout="30" />
</actions>
</resource-agent>
END

return $OCF_SUCCESS
}

exportfs_usage() {
	cat <<END
		usage: $0 {start|stop|monitor|status|validate-all|meta-data}
END
}

backup_rmtab ()
{
	grep :${OCF_RESKEY_directory}: $RMTAB > $FS_RMTAB
}

clean_rmtab ()
{
	REMOVE=`echo ${OCF_RESKEY_directory} | sed 's/\//\\\\\//g'`
	sed -i -e /:${REMOVE}:/d $RMTAB
}

exportfs_monitor ()
{
	grep -qs "${OCF_RESKEY_directory}" $RMTAB

#Adapt grep status code to OCF return code
	case $? in
	0) return $OCF_SUCCESS;;
	1) return $OCF_NOT_RUNNING;;
	*) return $OCF_ERR_GENERIC;;
	esac
}

exportfs_start ()
{
	ocf_log info "Exporting file system ..."

	if [ ${OCF_RESKEY_options} ]; then
		OPTIONS="${OCF_RESKEY_options}"
		OPTPREFIX=','
	fi
	if [ `echo ${OPTIONS} | grep fsid` ]; then
		#replace fsid provided in options list with one provided in fsid param.
		OPTIONS=`echo ${OPTIONS} | sed 's/fsid=[0-9]\+/fsid=${OCF_RESKEY_fsid}/g'`
	else
		#tack the fsid option onto our options list.
		OPTIONS="${OPTIONS}${OPTPREFIX}fsid=${OCF_RESKEY_fsid}"
	fi
	OPTIONS="-o ${OPTIONS}"

	ocf_run exportfs ${OPTIONS} ${OCF_RESKEY_clientspec}:${OCF_RESKEY_directory} || exit $OCF_ERR_GENERIC

	RETRIES=0
	while [ 1 ]; do
		showmount -e | grep -E "^${OCF_RESKEY_directory}[[:space:]]*${OCF_RESKEY_clientspec}$"
		rc=$?
		if [ $rc -eq 0 ]; then
			break
		fi
		RETRIES=`expr ${RETRIES} + 1`
		if  [ ${RETRIES} -eq 4 ]; then
			ocf_log debug "Export not reported by showmount -e"
			ocf_log err "Export not reported by showmount -e"
			return ${OCF_NOT_RUNNING}
		fi
		sleep 1
	done

	#restore saved rmtab backup from other server:
	if [ -f $FS_RMTAB ]; then
		cat $FS_RMTAB >> $RMTAB
		rm -f $FS_RMTAB
	fi

	#spawn our background process to backup the rmtab each 2 seconds
	/bin/sh $0 backup &

	ocf_log info "File system exported"
	return $OCF_SUCCESS
}

exportfs_stop ()
{
	ocf_log info "Un-exporting file system ..."

	ocf_run exportfs -u ${OCF_RESKEY_clientspec}:${OCF_RESKEY_directory}
	rc=$?

	if [ -f $PIDF ]; then
		kill `cat $PIDF`
		rm $PIDF
	fi

	backup_rmtab
	clean_rmtab

	if [ $rc -eq 0 ]; then
		ocf_log info "Un-exported file system"
		return $OCF_SUCCESS
	fi

	ocf_log err "Failed to un-export file system"
	exit $OCF_ERR_GENERIC
}

exportfs_backup ()
{
	echo $$ > $PIDF
	while [ 1 ]; do
		backup_rmtab
		sleep 2
	done
}

exportfs_validate ()
{
	# Checks for required parameters
	if [ -z "$OCF_RESKEY_directory" ]; then
		ocf_log err "Missing required parameter \"directory\""
		exit $OCF_ERR_CONFIGURED
	fi
	if [ -z "$OCF_RESKEY_fsid" ]; then
		ocf_log err "Missing required parameter \"fsid\""
		exit $OCF_ERR_CONFIGURED
	fi
	if [ -z "$OCF_RESKEY_clientspec" ]; then
		ocf_log err "Missing required parameter \"clientspec\""
		exit $OCF_ERR_CONFIGURED
	fi
	
	# Checks applicable only to non-probes
	if ! ocf_is_probe; then
		if [ ! -d $OCF_RESKEY_directory ]; then
			ocf_log err "$OCF_RESKEY_directory does not exist or is not a directory"
			exit $OCF_ERR_INSTALLED
		fi
	fi
}

if [ $# -ne 1 ]; then
	exportfs_usage
	exit $OCF_ERR_ARGS
fi

RMTAB=/var/lib/nfs/rmtab
PIDF=${OCF_RESKEY_directory}/.exportfs_backup.pid
FS_RMTAB=${OCF_RESKEY_directory}/.rmtab

case $__OCF_ACTION in
	meta-data)  exportfs_meta_data
		exit $OCF_SUCCESS
		;;
	usage|help) exportfs_usage
		exit $OCF_SUCCESS
		;;
	*)
		;;
esac

exportfs_validate

case $__OCF_ACTION in
	start)		exportfs_start
		;;
	stop)		exportfs_stop
		;;
	status|monitor)	exportfs_monitor
		;;
	backup)		exportfs_backup
		;;
	validate-all)
		# nothing to do -- we're already validated
		;;
	*)		exportfs_usage
			exit $OCF_ERR_UNIMPLEMENTED
		;;
esac
