# This is a script with common functions etc used by zfs-import, zfs-load-key, # zfs-mount, zfs-share and zfs-zed. # # It is _NOT_ to be called independently # # Released under the 2-clause BSD license. # # This script is based on debian/zfsutils.zfs.init from the # Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno. PATH=/sbin:/bin:/usr/bin:/usr/sbin # Source function library if [ -f /etc/rc.d/init.d/functions ]; then # RedHat and derivatives . /etc/rc.d/init.d/functions elif [ -L /etc/init.d/functions.sh ]; then # Gentoo . /etc/init.d/functions.sh elif [ -f /lib/lsb/init-functions ]; then # LSB, Debian, and derivatives . /lib/lsb/init-functions fi # Of course the functions we need are called differently # on different distributions - it would be way too easy # otherwise!! if type log_failure_msg > /dev/null 2>&1 ; then # LSB functions - fall through zfs_log_begin_msg() { log_begin_msg "$1"; } zfs_log_end_msg() { log_end_msg "$1"; } zfs_log_failure_msg() { log_failure_msg "$1"; } zfs_log_progress_msg() { log_progress_msg "$1"; } elif type success > /dev/null 2>&1 ; then # Fedora/RedHat functions zfs_set_ifs() { # For some reason, the init function library have a problem # with a changed IFS, so this function goes around that. local tIFS="$1" if [ -n "$tIFS" ] then TMP_IFS="$IFS" IFS="$tIFS" fi } zfs_log_begin_msg() { printf "%s" "$1 "; } zfs_log_end_msg() { # shellcheck disable=SC2154 zfs_set_ifs "$OLD_IFS" if [ "$1" -eq 0 ]; then success else failure fi echo zfs_set_ifs "$TMP_IFS" } zfs_log_failure_msg() { zfs_set_ifs "$OLD_IFS" failure echo zfs_set_ifs "$TMP_IFS" } zfs_log_progress_msg() { printf "%s" "$""$1"; } elif type einfo > /dev/null 2>&1 ; then # Gentoo functions zfs_log_begin_msg() { ebegin "$1"; } zfs_log_end_msg() { eend "$1"; } zfs_log_failure_msg() { eend "$1"; } # zfs_log_progress_msg() { printf "%s" "$1"; } zfs_log_progress_msg() { :; } else # Unknown - simple substitutes. zfs_log_begin_msg() { printf "%s" "$1"; } zfs_log_end_msg() { ret=$1 if [ "$ret" -ge 1 ]; then echo " failed!" else echo " success" fi return "$ret" } zfs_log_failure_msg() { echo "$1"; } zfs_log_progress_msg() { printf "%s" "$1"; } fi # Paths to what we need ZFS="@sbindir@/zfs" ZED="@sbindir@/zed" ZPOOL="@sbindir@/zpool" ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache" # Sensible defaults ZFS_LOAD_KEY='yes' ZFS_UNLOAD_KEY='no' ZFS_MOUNT='yes' ZFS_UNMOUNT='yes' ZFS_SHARE='yes' ZFS_UNSHARE='yes' # Source zfs configuration, overriding the defaults if [ -f @initconfdir@/zfs ]; then . @initconfdir@/zfs fi # ---------------------------------------------------- export ZFS ZED ZPOOL ZPOOL_CACHE ZFS_LOAD_KEY ZFS_UNLOAD_KEY ZFS_MOUNT ZFS_UNMOUNT \ ZFS_SHARE ZFS_UNSHARE zfs_action() { local MSG="$1"; shift local CMD="$*" local ret zfs_log_begin_msg "$MSG " $CMD ret=$? if [ "$ret" -eq 0 ]; then zfs_log_end_msg "$ret" else zfs_log_failure_msg "$ret" fi return "$ret" } # Returns # 0 if daemon has been started # 1 if daemon was already running # 2 if daemon could not be started # 3 if unsupported # zfs_daemon_start() { local PIDFILE="$1"; shift local DAEMON_BIN="$1"; shift if type start-stop-daemon > /dev/null 2>&1 ; then # LSB functions start-stop-daemon --start --quiet --pidfile "$PIDFILE" \ --exec "$DAEMON_BIN" --test > /dev/null || return 1 # shellcheck disable=SC2086 start-stop-daemon --start --quiet --exec "$DAEMON_BIN" -- \ "$@" || return 2 # On Debian, there's a 'sendsigs' script that will # kill basically everything quite early and zed is stopped # much later than that. We don't want zed to be among them, # so add the zed pid to list of pids to ignore. if [ -f "$PIDFILE" ] && [ -d /run/sendsigs.omit.d ] then ln -sf "$PIDFILE" /run/sendsigs.omit.d/zed fi elif type daemon > /dev/null 2>&1 ; then # Fedora/RedHat functions # shellcheck disable=SC2086 daemon --pidfile "$PIDFILE" "$DAEMON_BIN" "$@" return $? else # Unsupported return 3 fi return 0 } # Returns # 0 if daemon has been stopped # 1 if daemon was already stopped # 2 if daemon could not be stopped # 3 if unsupported # zfs_daemon_stop() { local PIDFILE="$1" local DAEMON_BIN="$2" local DAEMON_NAME="$3" if type start-stop-daemon > /dev/null 2>&1 ; then # LSB functions start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ --pidfile "$PIDFILE" --name "$DAEMON_NAME" ret="$?" [ "$ret" = 0 ] && rm -f "$PIDFILE" return "$ret" elif type killproc > /dev/null 2>&1 ; then # Fedora/RedHat functions killproc -p "$PIDFILE" "$DAEMON_NAME" ret="$?" [ "$ret" = 0 ] && rm -f "$PIDFILE" return "$ret" else # Unsupported return 3 fi return 0 } # Returns status zfs_daemon_status() { local PIDFILE="$1" local DAEMON_BIN="$2" local DAEMON_NAME="$3" if type status_of_proc > /dev/null 2>&1 ; then # LSB functions status_of_proc "$DAEMON_NAME" "$DAEMON_BIN" return $? elif type status > /dev/null 2>&1 ; then # Fedora/RedHat functions status -p "$PIDFILE" "$DAEMON_NAME" return $? else # Unsupported return 3 fi return 0 } zfs_daemon_reload() { local PIDFILE="$1" local DAEMON_NAME="$2" if type start-stop-daemon > /dev/null 2>&1 ; then # LSB functions start-stop-daemon --stop --signal 1 --quiet \ --pidfile "$PIDFILE" --name "$DAEMON_NAME" return $? elif type killproc > /dev/null 2>&1 ; then # Fedora/RedHat functions killproc -p "$PIDFILE" "$DAEMON_NAME" -HUP return $? else # Unsupported return 3 fi return 0 } zfs_installed() { if [ ! -x "$ZPOOL" ]; then return 1 else # Test if it works (will catch missing/broken libs etc) "$ZPOOL" -? > /dev/null 2>&1 return $? fi if [ ! -x "$ZFS" ]; then return 2 else # Test if it works (will catch missing/broken libs etc) "$ZFS" -? > /dev/null 2>&1 return $? fi return 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 } # Do a lot of checks to make sure it's 'safe' to continue with the import. checksystem() { if grep -qiE '(^|[^\\](\\\\)* )zfs=(off|no|0)( |$)' /proc/cmdline; then # Called with zfs=(off|no|0) - bail because we don't # want anything import, mounted or shared. # HOWEVER, only do this if we're called at the boot up # (from init), not if we're running interactively (as in # from the shell - we know what we're doing). # shellcheck disable=SC2154 [ -n "$init" ] && exit 3 fi # Check if ZFS is installed. zfs_installed || return 5 # Just make sure that /dev/zfs is created. udev_trigger return 0 } get_root_pool() { # shellcheck disable=SC2046 set -- $(mount | grep ' on / ') [ "$5" = "zfs" ] && echo "${1%%/*}" } # Check if a variable is 'yes' (any case) or '1' # Returns TRUE if set. check_boolean() { local var="$1" echo "$var" | grep -Eiq "^yes$|^on$|^true$|^1$" && return 0 || return 1 } check_module_loaded() { module="$1" [ -r "/sys/module/${module}/version" ] && return 0 || return 1 } load_module() { module="$1" # Load the zfs module stack if ! check_module_loaded "$module"; then if ! /sbin/modprobe "$module"; then return 5 fi fi return 0 } # first parameter is a regular expression that filters mtab read_mtab() { local match="$1" local fs mntpnt fstype opts rest # Unset all MTAB_* variables # shellcheck disable=SC2046 unset $(env | sed -e '/^MTAB_/!d' -e 's,=.*,,') while read -r fs mntpnt fstype opts rest; do if echo "$fs $mntpnt $fstype $opts" | grep -qE "$match"; then # * Fix problems (!?) in the mounts file. It will record # 'rpool 1' as 'rpool\0401' instead of 'rpool\00401' # which seems to be the correct (at least as far as # 'printf' is concerned). # * We need to use the external echo, because the # internal one would interpret the backslash code # (incorrectly), giving us a  instead. mntpnt=$(/bin/echo "$mntpnt" | sed 's,\\0,\\00,g') fs=$(/bin/echo "$fs" | sed 's,\\0,\\00,') # Remove 'unwanted' characters. mntpnt=$(printf '%b' "$mntpnt" | tr -d '/. -') fs=$(printf '%b' "$fs") # Set the variable. eval export "MTAB_$mntpnt=\"$fs\"" fi done < /proc/self/mounts } in_mtab() { local mntpnt="$1" # Remove 'unwanted' characters. mntpnt=$(printf '%b' "$mntpnt" | tr -d '/. -') local var var="$(eval echo "MTAB_$mntpnt")" [ "$(eval echo "$""$var")" != "" ] return "$?" } # first parameter is a regular expression that filters fstab read_fstab() { local match="$1" local i var # Unset all FSTAB_* variables # shellcheck disable=SC2046 unset $(env | sed -e '/^FSTAB_/!d' -e 's,=.*,,') i=0 while read -r fs mntpnt fstype opts; do echo "$fs" | grep -qE '^#|^$' && continue echo "$mntpnt" | grep -qE '^none|^swap' && continue echo "$fstype" | grep -qE '^swap' && continue if echo "$fs $mntpnt $fstype $opts" | grep -qE "$match"; then eval export "FSTAB_dev_$i=$fs" fs=$(printf '%b' "$fs" | tr '/' '_') eval export "FSTAB_$i=$mntpnt" i=$((i + 1)) fi done < /etc/fstab } in_fstab() { local var var="$(eval echo "FSTAB_$1")" [ "${var}" != "" ] return $? } is_mounted() { local mntpt="$1" local mp while read -r _ mp _; do [ "$mp" = "$mntpt" ] && return 0 done < /proc/self/mounts return 1 }