#!/bin/bash
#
# mount - mount and umount filesystems

wait_some()
{
	if [ -n "$1" ]; then
		sleep $1
	fi
}


run_without_msg()
{
	if [ -z "$1" ]; then
		echo -n "run_without_msg: missing command."
		echo -e $RESULT_FAIL
		return
	fi

	if [ -n "$DEBUG" ]; then
		echo -e "< $1 >\n"
		eval $1
	else
		eval $1 &> /dev/null
	fi

	local exit_status=$?

	wait_some "$2"

	return $exit_status
}


run_with_msg_and_exit_codes()
{
	# $1 : message
	# $2 : command
	# $3 : white space separated list of OK exit values
	# $4 : white space separated list of WARNING exit values
	# $5 : white separated list of FAILURE exit values

	if [ ! ${#} -eq 5 ]; then
		echo "run_with_special_exit_codes: wrong parameter count. 5 params expected"
		return 1
	fi

	echo "$1"

	if [ -n "${DEBUG}" ]; then
		echo -e "\n< run_with_special_exit_codes >"
		echo -e "\n< command : $2 >"
		echo -e "\n< valid   : $3 >"
		echo -e "\n< warn    : $4 >"
		echo -e "\n< fail    : $5 >"
		eval $2
	else
		eval $2
	fi

	local exit_status=$?

	for i in "$3"
	do
		if [ "$exit_status" = "$i" ]; then
			echo -e $RESULT_OK
			return $exit_status
		fi
	done

	for i in "$4"
	do
		if [ "$exit_status" = "$i" ]; then
			echo -e $RESULT_WARN
			return $exit_status
		fi
	done

	for i in "$5"
	do
		if [ "$exit_status" = "$i" ]; then
			echo -e $RESULT_FAIL
			return $exit_status
		fi
	done

	warn_msg "result $exit_status did not match any supplied exit code value"
	return $exit_status
}


run_with_msg()
{
	# message == $1
	# cmd == $2

	if [ -z "$1" ]; then
		echo -n "run_cmd_with_msg: missing message."
		echo -e $RESULT_WARN
		return
	fi

	if [ -z "$2" ]; then
		echo -n "run_cmd_with_msg: missing command."
		echo -e $RESULT_WARN
		return
	fi

	echo -n "$1"

	if [ -n "$DEBUG" ]; then
		 echo -n "< $2 >"
		 eval $2
	else
		 eval $2 &> /dev/null
	fi

	if [ $? -eq 0 ]; then
		echo -e $RESULT_OK
		wait_some $3
		return 0
	else
		echo -e $RESULT_FAIL
		wait_some $3
		return 1
	fi
}


warn_msg()
{
	echo -ne "$1$RESULT_WARN"
}

kernel_is_3()
{
	uname -r | grep -q "^3"
}

kernel_is_26()
{
	uname -r | grep -q "^2\.6"
}


kernel_is_24()
{
	uname -r | grep -q "^2\.4"
}


check_for_lvm()
{
  if [[ -x /sbin/vgscan ]]; then
	if [[ -e /proc/modules ]] && ! grep -qs 'device-mapper' /proc/{devices,misc}; then
 	  run_without_msg "modprobe dm-mod"
	fi
	if [[ -d /proc/lvm ]] || grep -qs 'device-mapper' /proc/{devices,misc}; then
	  run_with_msg " * Scanning for LVM volume groups" "/sbin/vgscan --mknodes --ignorelockingfailure"
	  if [[ $? -eq 0 ]] && [[ -d /etc/lvm ]] && [[ -x /sbin/vgchange ]]; then
		run_with_msg " * Initializing LVM volume groups" "/sbin/vgchange --ignorelockingfailure -a y"
	  fi
	fi
  fi
}

start()
{
	echo -e "Mounting filesystems:"

	run_with_msg " * Mounting /proc" "mount -n -t proc proc /proc"
        run_with_msg " * Mounting /run on /run" "mount -n -t tmpfs tmpfs /run -o size=4m,mode=0755"

	if ! grep -qw "ro" /proc/cmdline; then
		run_with_msg " * Remounting root read-only" "mount -n -o remount,ro /"
	fi

	DEVNODES=`grep -o "dev=.*" /proc/cmdline | cut -d" " -f1 | sed -e "s/dev=//g"`

	case $DEVNODES in
		static)
			;;
		udev)
			if kernel_is_24 ; then
				warn_message "No udev for 2.4 kernels. Kernel commandline parameter ignored."
				DEVNODES=""
			fi
			;;
		?*)
			warn_msg "Wrong device nodes parameter \"$DEVNODES\" on kernel line. Valid are: udev, static\nFalling back to default behaviour"
			DEVNODES=""
			;;
	esac

	# default bahaviour if the user did not choose himself
	if [ -z "$DEVNODES" ]; then
		if kernel_is_26 || kernel_is_3 ; then
			DEVNODES="udev"
			if [ -n "$DEBUG" ] ; then
				echo -e "\n< Found udev >"
			fi
		else
			warn_msg "Assuming a static /dev"
			DEVNODES="static"
		fi
	fi

	kernel_is_26 || kernel_is_3 && run_with_msg " * Mounting /sys" "mount -n -t sysfs sysfs /sys"

	if [ "$DEVNODES" = "udev" ]; then
        run_with_msg " * Mounting udev on /dev" "mount -w -n -t tmpfs udevfs /dev -o size=6m,mode=0755"
        # Creating "must have" static device nodes
        run_without_msg "mkdir -p /dev/{pts,shm}"
        run_without_msg "cp -a /lib/udev/devices/* /dev"
        if [ ! -h /dev/ram ]; then
            ln -sf /dev/ram0 /dev/ram
        fi
	run_without_msg "echo > /proc/sys/kernel/hotplug" # Hotplug agents are deprecated!
	run_with_msg "Starting udev device handling daemon version `udevd --version`" "/sbin/udevd --daemon"
        run_with_msg "Activating coldplug devices" "/sbin/udevadm trigger"
        run_with_msg "Devices activation - please wait" "/sbin/udevadm settle"
        # This is first aid code for devices hard to init (too slow or to buggy for init by 1st time)
        # I never have to use it as my machine works great - I leave it just in case
        #run_with_msg "Activating lazy devices" "/sbin/udevadm trigger --type=failed"
        #run_with_msg "Lazy devices activation - please wait" "/sbin/udevadm settle"
		if [ ! -h /dev/fd ]; then
			ln -sf /proc/self/fd /dev/fd
		fi

		ln -snf /proc/self/fd/0 /dev/stdin
		ln -snf /proc/self/fd/1 /dev/stdout
		ln -snf /proc/self/fd/2 /dev/stderr
	fi

	# or do we want to explicitly _not_ fsck at all
	if ! grep -qw nofsck /proc/cmdline ; then
		# force fsck run?
		if [ -f /forcefsck ] || grep -qw forcefsck /proc/cmdline ; then
			FORCE="-f"
		fi

		# Check root fs first so we can remount rw or LVM will fail
		run_with_msg_and_exit_codes "Checking root filesystem: " "fsck -C -p -T $FORCE /" "0" "1" ""
        if [ $? -eq 3 ]; then
		  # Filesystem errors corrected, and system should be rebooted
          reboot -f
		elif [ $? -ge 2 ]; then
		    echo ""
		    echo " *** fsck failed! ***"
		    echo ""
		    echo "   Please repair your file system manually by"
		    echo "   running /sbin/fsck without the -p option."
		    echo ""
		    sulogin
		    reboot  -f
		fi

	    run_with_msg " * Remounting root read-write" "mount -n -o remount,rw /"

		# Time to init LVM
		check_for_lvm

		# check filesystems
		run_with_msg_and_exit_codes "Checking other filesystems: " "fsck -A -T -C -p -s -R $FORCE" "0" "1" ""
		if [ $? -eq 3 ]; then
			# Filesystem errors corrected, and system should be rebooted
			reboot  -f
		elif [ $? -ge 2 ]; then
		    echo ""
		    echo " *** fsck failed! ***"
		    echo ""
		    echo "   Please repair your file system manually by"
		    echo "   running /sbin/fsck without the -p option."
		    echo ""
		    sulogin
		    reboot  -f
		fi
    else
	  run_with_msg " * Remounting root read-write" "mount -n -o remount,rw /"
      check_for_lvm
	fi

	if [ "$FORCE" == "-f" ]; then
		rm -f /forcefsck
	fi

	run_without_msg "> /etc/mtab"
	# hack to get it into /etc/mtab
 	run_without_msg "mount -f -o remount,rw /"
	run_with_msg  "Turning on swap" "swapon -a"

	# Mount everything except NET_FS and a few others
	NET_FS="nosmbfs nocifs nonfs nonfs4 nocoda noafs nogfs noncpfs noshfs"
	run_with_msg "Mounting local filesystems:" "mount -a -t notmpfs,noproc,no${NET_FS// /,}"
	# Mount all tmpfs devices after all physical
	run_without_msg "mount -a -t tmpfs"

}


stop()
{
	# Establish where to get mount table from, save these since the files change if we unmount something
	if [ -r /proc/mounts ] ; then
		MOUNTS=/proc/mounts
	else
		MOUNTS=/etc/mtab
	fi

	# write wtmp in /var before umounting /var
	run_without_msg "reboot -w"

	echo "Unmounting all filesystems:"

	tac $MOUNTS | while read TYPE PNT FS REST ; do

		case $FS in
			nfs|nfs4|smbfs) # we don't do net filesystems
				continue
				;;
		esac

		case $PNT in
			/|/proc|/dev|/sys) # no need to unmount these
				continue
				;;
		esac

		echo  -n " * Umounting $PNT"
		run_without_msg "sync ; sync" # Flush buffers

		ITERATION=1
		UNMOUNT_ERROR=0

		until [ -z `grep -w $PNT $MOUNTS | awk '{ print $1 }'` ] || [ $UNMOUNT_ERROR -eq 1 ];
		do
			case $ITERATION in
			  1)
				 run_without_msg "umount $PNT"
				 ;;
			  2)
				 sleep 1
				 run_without_msg "fuser -s -km -3 $PNT" "1"
				 run_without_msg "umount $PNT"
				 ;;
			  3)
				 sleep 1
				 run_without_msg "fuser -s -km -9 $PNT" "1"
				 run_without_msg "umount $PNT" "1"
				 ;;
			  4)
				 sleep 1
				 run_without_msg "fuser -s -km -9 $PNT" "1"
				 run_without_msg "umount -lf $PNT" "4"
				 run_without_msg "sync; sync"
				 ;;
			  5)
				 UNMOUNT_ERROR=1 # warn that it hasn't been able to unmount the easy way
				 ;;
			 esac

			 (( ITERATION++ ))
		done

		if [ $UNMOUNT_ERROR -eq 1 ]; then # haven't been able to unmount the point, so be drastic
			echo -e $RESULT_WARN
			run_with_msg "  * Attempting to remount $PNT read-only" "mount $PNT -o remount,ro"
		else
			echo -e $RESULT_OK
		fi

		if [ "$TYPE" == "/dev/loop*" -a -f $PNT ] ; then
			# unhook loopback device too
			run_with_msg "Detaching loopback device $TYPE" "losetup -d $TYPE"
		fi

	done

	run_with_msg "Turning off swap" "swapoff -a"

	run_without_msg "sync; sync;" "2"  # might take some time so we wait here for a couple of seconds

	if ! run_with_msg  " * Remounting root read-only"  "mount -n -o remount,ro /" ; then
		if ! run_with_msg  " * Trying again to remount root read-only"  "umount -l -O remount,ro /" "4" ; then
			read -n 1 -t 30 -p "Do you want to login? (y/n) "  CONFIRM
			echo   ""
			case $CONFIRM in
				y|Y)  sulogin ;;
			esac
		fi
	fi
}


# to avoid confusion we force only these options as being valid:
case "$1" in
	start|stop)
		;;

	*)
		echo "Warning: $0 should never be called directly";
		exit 1
		;;
esac

. /lib/lsb/init-functions
