Update zfs.fedora init script
authorManuel Amador (Rudd-O) <rudd-o@rudd-o.com>
Thu, 7 Apr 2011 17:34:20 +0000 (10:34 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Thu, 7 Apr 2011 17:48:11 +0000 (10:48 -0700)
Apply all of Rudd-O's changes for the Fedora init script.  The
initial init script was one I threw together based on Rudd-O's
original work.  It worked for me but it has some flaws.

Rudd-O has invested considerable time updating it to be significantly
smarter.  It now handles using ZFS as your root filesystem plus
various other quirks.  Since he is familiar with the right
way to do things on Fedora and has tested this init script we
are integrating all of his changes.

Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
etc/init.d/zfs.fedora

index 2751603..8e48efb 100644 (file)
@@ -11,8 +11,8 @@
 #
 ### BEGIN INIT INFO
 # Provides: zfs
-# Required-Start: $local_fs
-# Required-Stop: $local_fs
+# Required-Start:
+# Required-Stop:
 # Should-Start:
 # Should-Stop:
 # Default-Start: 2 3 4 5
 #              filesystems and starts all related zfs services.
 ### END INIT INFO
 
-# Source function library.
-. /etc/rc.d/init.d/functions
+export PATH=/usr/local/sbin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin
 
-# Source zfs configuration.
-[ -f /etc/sysconfig/zfs ] &&  . /etc/sysconfig/zfs
+# Source function library & LSB routines
+. /etc/rc.d/init.d/functions
 
+# script variables
 RETVAL=0
+ZPOOL=zpool
+ZFS=zfs
+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
+}
 
-LOCKFILE=/var/lock/subsys/zfs
-CACHEFILE=/etc/zfs/zpool.cache
-ZPOOL=/usr/sbin/zpool
-ZFS=/usr/sbin/zfs
+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
+}
 
-[ -x $ZPOOL ] || exit 1
-[ -x $ZFS ] || exit 2
+# 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
 
-start()
-{
-       [ -f $LOCKFILE ] && return 3
+declare -A MTAB
+declare -A FSTAB
 
-       # 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 4
-       fi
+# 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)
+}
 
-       # Load the zfs module stack
-       /sbin/modprobe zfs
+in_mtab() {
+        [ "${MTAB[$1]}" != "" ]
+        return $?
+}
 
-       # Ensure / exists in /etc/mtab, if not update mtab accordingly.
-       # This should be handled by rc.sysinit but lets be paranoid.
-       awk '$2 == "/" { exit 1 }' /etc/mtab
-       RETVAL=$?
-       if [ $RETVAL -eq 0 ]; then
-               /bin/mount -f /
-       fi
+# 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)
+}
 
-       # Import all pools described by the cache file, and then mount
-       # all filesystem based on their properties.
-        if [ -f $CACHEFILE ] ; then
-               action $"Importing ZFS pools: " \
-                       $ZPOOL import -c $CACHEFILE -aN 2>/dev/null
-               action $"Mounting ZFS filesystems: " \
-                       $ZFS mount -a
-       fi
-       
-       touch $LOCKFILE
+in_fstab() {
+        [ "${FSTAB[$1]}" != "" ]
+        return $?
 }
 
-stop()
+start()
 {
-       [ ! -f $LOCKFILE ] && return 3
+       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
+
+       # load kernel module infrastructure
+       if ! grep -q zfs /proc/modules ; then
+               action $"Loading kernel ZFS infrastructure: " modprobe zfs || return 5
+       fi
 
-       action $"Unmounting ZFS filesystems: " $ZFS umount -a
+       # 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
 
-       rm -f $LOCKFILE
+        if [ -f /etc/zfs/zpool.cache ] ; then
+       
+               echo -n $"Importing ZFS pools not yet imported: "
+               $ZPOOL import -c /etc/zfs/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
+
+       # hack to read mounted file systems because otherwise
+       # zfs returns EPERM when a non-root user reads a mounted filesystem before root did
+       savepwd="$PWD"
+       mount | grep " type zfs " | sed 's/.*on //' | sed 's/ type zfs.*$//' | while read line ; do
+               cd "$line" > /dev/null 2>&1
+               ls > /dev/null
+       done
+       cd "$savepwd"
+
+       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"
 }
 
-status()
+stop()
 {
-       [ ! -f $LOCKFILE ] && return 3
-
-       $ZPOOL status && echo && $ZPOOL list
+       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
@@ -99,21 +223,24 @@ case "$1" in
                RETVAL=$?
                ;;
        status)
-               status
-               RETVAL=$?
+               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
+               if [ -f "$LOCKFILE" ] ; then
                        stop
                        start
                fi
                ;;
        *)
                echo $"Usage: $0 {start|stop|status|restart|condrestart}"
+               RETVAL=3
                ;;
 esac