#!/bin/bash
#
# zfs           This script will mount/umount the zfs filesystems.
#
# chkconfig:    2345 01 99
# description:  This script will mount/umount the zfs filesystems during
#               system boot/shutdown.  Configuration of which filesystems
#               should be mounted is handled by the zfs 'mountpoint' and
#               'canmount' properties.  See the zfs(8) man page for details.
#               It is also responsible for all userspace zfs services.
#
### BEGIN INIT INFO
# Provides: zfs
# Required-Start:
# Required-Stop:
# Should-Start:
# Should-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 1
# Short-Description: Mount/umount the zfs filesystems
# Description: ZFS is an advanced filesystem designed to simplify managing
#              and protecting your data.  This service mounts the ZFS
#              filesystems and starts all related zfs services.
### END INIT INFO

export PATH=/usr/local/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin

if [ -z "$init" ]; then
    # Not interactive
    grep -Eqi 'zfs=off|zfs=no' /proc/cmdline && exit 3
fi

# Source function library & LSB routines
. /etc/rc.d/init.d/functions

# script variables
RETVAL=0
ZFS="@sbindir@/zfs"
ZPOOL="@sbindir@/zpool"
ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache"
servicename=zfs
LOCKFILE=/var/lock/subsys/$servicename

# functions
zfs_installed() {
	modinfo zfs > /dev/null 2>&1 || return 5
	$ZPOOL  > /dev/null 2>&1
	[ $? == 127 ] && return 5
	$ZFS    > /dev/null 2>&1
	[ $? == 127 ] && return 5
	return 0
}

reregister_mounts() {
	cat /etc/mtab | while read -r fs mntpnt fstype opts rest ; do
		fs=`printf '%b\n' "$fs"`
		mntpnt=`printf '%b\n' "$mntpnt"`
		if [ "$fstype" == "zfs" ] ; then
			if [ "$mntpnt" == "/" ] ; then
				mount -f -o zfsutil -t zfs --move / /removethismountpointhoweverpossible
				umount --fake /removethismountpointhoweverpossible
			else
				umount --fake "$mntpnt"
			fi
		elif echo "$fs" | grep -q "^/dev/zd" ; then
			if [ "$mntpnt" == "/" ] ; then
				mount -f -t "$fstype" --move / /removethismountpointhoweverpossible
				umount --fake /removethismountpointhoweverpossible
			else
				umount --fake "$mntpnt"
			fi
		fi
	done
	cat /proc/mounts | while read -r fs mntpnt fstype opts rest ; do
		fs=`printf '%b\n' "$fs"`
		mntpnt=`printf '%b\n' "$mntpnt"`
		if [ "$fstype" == "zfs" ] ; then
			mount -f -t zfs -o zfsutil "$fs" "$mntpnt"
		elif echo "$fs" | grep -q "^/dev/zd" ; then
			mount -f -t "$fstype" -o "$opts" "$fs" "$mntpnt"
		fi
	done
}

# i need a bash guru to simplify this, since this is copy and paste, but donno how
# to correctly dereference variable names in bash, or how to do this right

declare -A MTAB
declare -A FSTAB

# first parameter is a regular expression that filters mtab
read_mtab() {
        for fs in "${!MTAB[@]}" ; do unset MTAB["$fs"] ; done
        while read -r fs mntpnt fstype opts blah ; do
                fs=`printf '%b\n' "$fs"`
                MTAB["$fs"]=$mntpnt
        done < <(grep "$1" /etc/mtab)
}

in_mtab() {
        [ "${MTAB[$1]}" != "" ]
        return $?
}

# first parameter is a regular expression that filters fstab
read_fstab() {
        for fs in "${!FSTAB[@]}" ; do unset FSTAB["$fs"] ; done
        while read -r fs mntpnt fstype opts blah ; do
                fs=`printf '%b\n' "$fs"`
                FSTAB["$fs"]=$mntpnt
        done < <(grep "$1" /etc/fstab)
}

in_fstab() {
        [ "${FSTAB[$1]}" != "" ]
        return $?
}

start()
{
	if [ -f "$LOCKFILE" ] ; then return 0 ; fi

	# check if ZFS is installed.  If not, comply to FC standards and bail
	zfs_installed || {
		action $"Checking if ZFS is installed: not installed" /bin/false
		return 5
	}

        # Requires selinux policy which has not been written.
        if [ -r "/selinux/enforce" ] &&
           [ "$(cat /selinux/enforce)" = "1" ]; then
                action $"SELinux ZFS policy required: " /bin/false || return 6
        fi

	# Delay until all required block devices are present.
	udevadm settle

	# load kernel module infrastructure
	if ! grep -q zfs /proc/modules ; then
		action $"Loading kernel ZFS infrastructure: " modprobe zfs || return 5
	fi

	# fix mtab to include already-mounted fs filesystems, in case there are any
	# we ONLY do this if mtab does not point to /proc/mounts
	# which is the case in some systems (systemd may bring that soon)
	if ! readlink /etc/mtab | grep -q /proc ; then
		if grep -qE "(^/dev/zd| zfs )" /proc/mounts ; then
			action $"Registering already-mounted ZFS filesystems and volumes: " reregister_mounts || return 150
		fi
	fi

        if [ -f $ZPOOL_CACHE ] ; then

		echo -n $"Importing ZFS pools not yet imported: "
		$ZPOOL import -c $ZPOOL_CACHE -aN || true # stupid zpool will fail if all pools are already imported
		RETVAL=$?
		if [ $RETVAL -ne 0 ]; then
			failure "Importing ZFS pools not yet imported: "
			return 151
		fi
		success "Importing ZFS pools not yet imported: "

	fi

	action $"Mounting ZFS filesystems not yet mounted: " $ZFS mount -a || return 152

	action $"Exporting ZFS filesystems: " $ZFS share -a || return 153

	read_mtab  "^/dev/zd"
	read_fstab "^/dev/zd"

	template=$"Mounting volume %s registered in fstab: "
	for volume in "${!FSTAB[@]}" ; do
		if in_mtab "$volume" ; then continue ; fi
		string=`printf "$template" "$volume"`
		action "$string" mount "$volume"
	done

	touch "$LOCKFILE"
}

stop()
{
	if [ ! -f "$LOCKFILE" ] ; then return 0 ; fi

	# check if ZFS is installed.  If not, comply to FC standards and bail
	zfs_installed || {
		action $"Checking if ZFS is installed: not installed" /bin/false
		return 5
	}

	# the poweroff of the system takes care of this
	# but it never unmounts the root filesystem itself
	# shit

	action $"Syncing ZFS filesystems: " sync
	     # about the only thing we can do, and then we
	     # hope that the umount process will succeed
	     # unfortunately the umount process does not dismount
	     # the root file system, there ought to be some way
	     # we can tell zfs to just flush anything in memory
	     # when a request to remount,ro comes in

	#echo -n $"Unmounting ZFS filesystems: "
	#$ZFS umount -a
	#RETVAL=$?
	#if [ $RETVAL -ne 0 ]; then
	#	failure

	#	return 8
	#fi
	#success

	rm -f "$LOCKFILE"
}

# See how we are called
case "$1" in
	start)
		start
		RETVAL=$?
		;;
	stop)
		stop
		RETVAL=$?
		;;
	status)
		lsmod | grep -q zfs || RETVAL=3
		$ZPOOL status && echo && $ZFS list || {
			[ -f "$LOCKFILE" ] && RETVAL=2 || RETVAL=4
		}
		;;
	restart)
		stop
		start
		;;
	condrestart)
		if [ -f "$LOCKFILE" ] ; then
			stop
			start
		fi
		;;
	*)
		echo $"Usage: $0 {start|stop|status|restart|condrestart}"
		RETVAL=3
		;;
esac

exit $RETVAL