mirror of
https://git.proxmox.com/git/mirror_zfs.git
synced 2024-12-27 03:19:35 +03:00
ae66d3aa90
Strengthen static code analysis for shell scripts. Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Reviewed-by: John Kennedy <john.kennedy@delphix.com> Signed-off-by: szubersk <szuberskidamian@gmail.com> Closes #12913
434 lines
9.3 KiB
Plaintext
434 lines
9.3 KiB
Plaintext
# 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
|
||
}
|