Skip to content

Commit

Permalink
Base init scripts for SYSV systems
Browse files Browse the repository at this point in the history
* Based on the init scripts included with Debian GNU/Linux, then take code from the
  already existing ones, trying to merge them into one for better maintainability.
  + Merge openzfs#2148 - Inform OpenRC that ZFS uses mtab.
  + Add a configurable ZFS_INITRD_POST_MODPROBE_SLEEP used in the initrd to sleep after the modprobe.
  + The import function, do_import(), imports pools by name instead of '-a' [all].
    + Test all '/dev/disk/by-*' dirs for import. Include /dev as a last ditch attempt.
  + Fallback on importing the pool using the cache file (if it exists) if the
    'by-id' didn't work.
  + Add exceptions to pool imports.
  + ZED script from the Debian GNU/Linux packages added.

Signed-off-by: Turbo Fredriksson [email protected]
Closes: openzfs#2974, openzfs#2107.
  • Loading branch information
FransUrbo committed Apr 30, 2015
1 parent 6186e29 commit 2b24a71
Show file tree
Hide file tree
Showing 14 changed files with 961 additions and 786 deletions.
9 changes: 9 additions & 0 deletions config/zfs-build.m4
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,15 @@ AC_DEFUN([ZFS_AC_DEFAULT_PACKAGE], [
AC_MSG_RESULT([$DEFAULT_INIT_SCRIPT])
AC_SUBST(DEFAULT_INIT_SCRIPT)
AC_MSG_CHECKING([default init config direectory])
AS_IF([test -d "/etc/default"], [
DEFAULT_INITCONF_DIR="/etc/default"
], [test -d "/etc/sysconfig"], [
DEFAULT_INITCONF_DIR="/etc/sysconfig"
])
AC_MSG_RESULT([$DEFAULT_INITCONF_DIR])
AC_SUBST(DEFAULT_INITCONF_DIR)

This comment has been minimized.

Copy link
@behlendorf

behlendorf Apr 30, 2015

Nice, we definitely need this but unfortunately we can't rely on the directory test to get this right. My stock CentOS 6 system does have an /etc/default directory with one mispackaged file which throws this off. It would be more reliable to add a case "$VENDOR" in ... esac block similar two the other two in this block.

This comment has been minimized.

Copy link
@FransUrbo

FransUrbo Apr 30, 2015

Author Owner

Ok. So which uses /etc/default and who uses /etc/sysconfig? lsb => /etc/default and /etc/sysconfig otherwise?

This comment has been minimized.

Copy link
@behlendorf

behlendorf Apr 30, 2015

Here's a swag at it.

-       AS_IF([test -d "/etc/default"], [
-               DEFAULT_INITCONF_DIR="/etc/default"
-       ], [test -d "/etc/sysconfig"], [
-               DEFAULT_INITCONF_DIR="/etc/sysconfig"
-       ])
+       case "$VENDOR" in
+               toss)       DEFAULT_INITCONF_DIR=/etc/sysconfig ;;
+               redhat)     DEFAULT_INITCONF_DIR=/etc/sysconfig ;;
+               fedora)     DEFAULT_INITCONF_DIR=/etc/sysconfig ;;
+               sles)       DEFAULT_INITCONF_DIR=/etc/sysconfig ;;
+               ubuntu)     DEFAULT_INITCONF_DIR=/etc/default   ;;
+               debian)     DEFAULT_INITCONF_DIR=/etc/default   ;;
+               *)          DEFAULT_INITCONF_DIR=/etc/default   ;;
+       esac

This comment has been minimized.

Copy link
@FransUrbo

FransUrbo Apr 30, 2015

Author Owner

Nice! Thanx.

])

dnl #

This comment has been minimized.

Copy link
@behlendorf

behlendorf Apr 30, 2015

The following should be updated in the zfs.spec.in file:

diff --git a/rpm/generic/zfs.spec.in b/rpm/generic/zfs.spec.in
index c9bf36e..184a228 100644
--- a/rpm/generic/zfs.spec.in
+++ b/rpm/generic/zfs.spec.in
@@ -229,16 +229,24 @@ find %{?buildroot}%{_libdir} -name '*.la' -exec rm -f {} \
 %if 0%{?_systemd}
 %systemd_post zfs.target
 %else
-[ -x /sbin/chkconfig ] && /sbin/chkconfig --add zfs
-%endif
+if [ -x /sbin/chkconfig ]; then
+    /sbin/chkconfig --add zfs-import
+    /sbin/chkconfig --add zfs-mount
+    /sbin/chkconfig --add zfs-share
+    /sbin/chkconfig --add zfs-zed
+fi
+ndif
 exit 0

 %preun
 %if 0%{?_systemd}
 %systemd_preun zfs.target
 %else
-if [ $1 -eq 0 ] ; then
-    [ -x /sbin/chkconfig ] && /sbin/chkconfig --del zfs
+if [ $1 -eq 0 ] && [ -x /sbin/chkconfig ]; then
+    /sbin/chkconfig --del zfs-import
+    /sbin/chkconfig --del zfs-mount
+    /sbin/chkconfig --del zfs-share
+    /sbin/chkconfig --del zfs-zed
 fi
 %endif
 exit 0
Expand Down
5 changes: 5 additions & 0 deletions etc/init.d/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
common.init
zfs-import
zfs-mount
zfs-share
zfs-zed
zfs
48 changes: 32 additions & 16 deletions etc/init.d/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
initdir = $(DEFAULT_INIT_DIR)
init_SCRIPTS = zfs
init_SCRIPTS = zfs-import zfs-mount zfs-share zfs-zed

initcommondir = $(sysconfdir)/zfs
initcommon_SCRIPTS = common.init

initconfdir = $(DEFAULT_INITCONF_DIR)
initconf_SCRIPTS = zfs

EXTRA_DIST = \
$(top_srcdir)/etc/init.d/zfs.fedora.in \
$(top_srcdir)/etc/init.d/zfs.gentoo.in \
$(top_srcdir)/etc/init.d/zfs.lsb.in \
$(top_srcdir)/etc/init.d/zfs.lunar.in \
$(top_srcdir)/etc/init.d/zfs.redhat.in
$(top_srcdir)/etc/init.d/common.init.in \
$(top_srcdir)/etc/init.d/zfs-share.in \
$(top_srcdir)/etc/init.d/zfs-import.in \
$(top_srcdir)/etc/init.d/zfs-mount.in \
$(top_srcdir)/etc/init.d/zfs-zed.in \
$(top_srcdir)/etc/init.d/zfs.in

$(init_SCRIPTS): $(init_SCRIPTS).$(DEFAULT_INIT_SCRIPT).in
-$(SED) -e 's,@bindir\@,$(bindir),g' \
-e 's,@sbindir\@,$(sbindir),g' \
-e 's,@udevdir\@,$(udevdir),g' \
-e 's,@udevruledir\@,$(udevruledir),g' \
-e 's,@sysconfdir\@,$(sysconfdir),g' \
-e 's,@initdir\@,$(initdir),g' \
-e 's,@runstatedir\@,$(runstatedir),g' \
'$@.$(DEFAULT_INIT_SCRIPT).in' >'$@'
$(init_SCRIPTS) $(initconf_SCRIPTS) $(initcommon_SCRIPTS):
-(if [ -e /etc/debian_version ]; then \
NFS_SRV=nfs-kernel-server; \
else \
NFS_SRV=nfs; \
fi; \
$(SED) -e 's,@bindir\@,$(bindir),g' \
-e 's,@sbindir\@,$(sbindir),g' \
-e 's,@udevdir\@,$(udevdir),g' \
-e 's,@udevruledir\@,$(udevruledir),g' \
-e 's,@sysconfdir\@,$(sysconfdir),g' \
-e 's,@initconfdir\@,$(initconfdir),g' \
-e 's,@initdir\@,$(initdir),g' \
-e 's,@runstatedir\@,$(runstatedir),g' \
-e "s,@NFS_SRV\@,$$NFS_SRV,g" \
'[email protected]' >'$@'; \
[ '$@' = 'common.init' -o '$@' = 'zfs' ] || \
chmod +x '$@')

distclean-local::
-$(RM) $(init_SCRIPTS)
-$(RM) $(init_SCRIPTS) $(initcommon_SCRIPTS) $(initconf_SCRIPTS)
250 changes: 250 additions & 0 deletions etc/init.d/common.init.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
# This is a script with common functions etc used by zfs-mount and zfs-share.
#
# It is _NOT_ to be called independently

PATH=/sbin:/bin:/usr/bin:/usr/sbin

# Source function library

This comment has been minimized.

Copy link
@behlendorf

behlendorf Apr 30, 2015

Unfortunately the logging function as they are now won't work for a non-lsb setup. Let me propose something I think will work reasonably well. Let's add a zfs_action wrapper function which handles running the command and logging success or failure as appropriate. The zfs_success_msg and zfs_failure_msg variables could them be optionally defined to provide an additional error message. Here's a mostly fleshed out example, with the redhat chunk lightly tested.

# Source function library
if [ -f /lib/lsb/init-functions ]; then
        . /lib/lsb/init-functions
        INIT_STYLE="lsb"
elif [ -f /etc/rc.d/init.d/functions ]; then
        . /etc/rc.d/init.d/functions
        INIT_STYLE="redhat"
else
        echo "Fatal: Init library functions not found"
        exit 1
fi

zfs_success_msg="success"
zfs_success_msg="failure"

zfs_action() {
        local STRING rc

        case "$INIT_STYLE" in
        lsb)
                # UNTESTED: THIS IS JUST AN EXAMPLE OF HOW IT COULD BE DONE
                STRING=$1
                log_begin_msg "$STRING "
                shift
                "$@" && \
                    log_success_msg "$zfs_success_msg" || \
                    log_failure_msg "$zfs_failure_msg"
                rc=$?
                echo
                ;;
        redhat)
                action "$@"
                rc=$?
                ;;
        *)
                echo "Fatal: Unknown init system: $INIT_STYLE"
                exit 1
                ;;
        esac

        return $rc
}

zfs_installed() {
        if [ ! -x $ZPOOL ]; then
                zfs_failure_msg="$ZPOOL binary not found."
                return 1
        fi
        if [ ! -x $ZFS ]; then
                zfs=failure_msg="$ZFS binary not found."
                return 1
        fi

        return 0
}

zfs_action "Checking ZFS utilites tools" zfs_installed || return 5

This comment has been minimized.

Copy link
@FransUrbo

FransUrbo via email Apr 30, 2015

Author Owner
if [ -f /lib/lsb/init-functions ]; then
. /lib/lsb/init-functions
elif [ -f /etc/rc.d/init.d/functions ]; then
. /etc/rc.d/init.d/functions
fi

# Of course the functions we need is called differently
# on different distributions - it would be way to easy
# otherwise!!
if type log_failure_msg > /dev/null ; then
# LSB functions
log_begin_msg=log_begin_msg
log_failure_msg=log_failure_msg
log_progress_msg=log_progress_msg
elif type success > /dev/null ; then
# Fedora/RedHat functions
log_begin_msg=success
log_failure_msg=failure
log_progress_msg=echo -n
elif type einfo > /dev/null ; then
# Gentoo functions
log_begin_msg=einfo
log_failure_msg=eerror
log_progress_msg=echo -n
else
log_begin_msg=echo -n
log_failure_msg=echo
log_progress_msg=echo -n
fi

# The log_end_msg is a little different - it's both an
# echo of a failed message and a return of a code number.
# So if it doesn't exist, we define a very simple one
# that would do the work.
if ! type log_end_msg > /dev/null ; then
log_end_msg() {
ret=$1
if [ "$ret" -ge 1 ]; then
echo " failed!"
else
echo " success"
fi
return $ret
}
fi
log_end_msg=log_end_msg

# Paths to what we need
ZFS="@sbindir@/zfs"
ZED="@sbindir@/zed"
ZPOOL="@sbindir@/zpool"
ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache"

# Sensible defaults
ZFS_MOUNT='yes'
ZFS_UNMOUNT='yes'
LOCKDIR=/run/lock

# Source zfs configuration, overriding the defaults
if [ -f /etc/@initconfdir@/zfs ]; then
. /etc/@initconfdir@/zfs
fi

[ ! -d "$LOCKDIR" ] && mkdir $LOCKDIR

# ----------------------------------------------------

zfs_installed() {
$log_begin_msg "Checking if zfs userspace tools present"
if [ ! -x $ZPOOL ]; then
$log_failure_msg "$ZPOOL binary not found."
$log_end_msg 1
fi
if [ ! -x $ZFS ]; then
$log_failure_msg "$ZFS binary not found."
$log_end_msg 1
fi
$log_end_msg 0
}

# Trigger udev and wait for it to settle.
udev_trigger() {
if [ -x /sbin/udevadm ]; then
/sbin/udevadm trigger --action=change --subsystem-match=block
/sbin/udevadm settle
elif [ -x /sbin/udevsettle ]; then
/sbin/udevtrigger
/sbin/udevsettle
fi
}

# From scripts/common.sh
wait_udev() {
local DEVICE=$1
local DELAY=$2
local COUNT=0

udev_trigger
while [ ! -e ${DEVICE} ]; do
if [ ${COUNT} -gt ${DELAY} ]; then
return 1
fi

let COUNT=${COUNT}+1
sleep 1
done

return 0
}

# Do a lot of checks to make sure it's 'safe' to continue with import/mount etc
checksystem()
{
if [ -z "$init" ]; then
# Not interactive and we don't want to import pool or mount filesystems.
# Won't of course work if you're booting from ZFS...
grep -qiE '(^|[^\\](\\\\)* )zfs=(off|no|0)( |$)' /proc/cmdline && exit 3
fi

[ -f "$LOCKDIR/$servicename" ] && return 3

# Check if ZFS is installed. If not, comply to FC standards and bail
zfs_installed || {
$log_failure_msg "not installed"
return 5
}

# Delay until all required block devices are present.
if [ -x /sbin/udevadm ]; then
/sbin/udevadm settle
elif [ -x /sbin/udevsettle ]; then
/sbin/udevsettle
fi

This comment has been minimized.

Copy link
@behlendorf

behlendorf Apr 30, 2015

This block can be replaced with the udev_trigger helper.


# Load the zfs module stack
if ! grep -q zfs /proc/modules ; then
$log_begin_msg "Loading kernel ZFS infrastructure: "
modprobe zfs || {
$log_failure_msg "Could not load zfs module"
$log_end_msg 1
return 5
}
$log_end_msg 0
fi

# Just make sure that /dev/zfs is created.
wait_udev /dev/zfs 15

# 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|^/dev/zvol| zfs )" /proc/mounts ; then
$log_begin_msg "Registering already-mounted ZFS filesystems and volumes: "
reregister_mounts || {
$log_end_msg 1
return 150
}
fi
fi

# 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

if ! [ `uname -m` == "x86_64" ]; then
echo "Warning: You're not running 64bit. Currently native zfs in";
echo " linux is only supported and tested on 64bit.";
# should we break here? People doing this should know what they
# do, thus i'm not breaking here.
fi
}

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 -qE "^/dev/(zd|zvol)" ; 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 -E "$1" /etc/mtab)
}

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

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

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

0 comments on commit 2b24a71

Please sign in to comment.