mirror of
				https://git.proxmox.com/git/mirror_zfs
				synced 2025-10-31 20:33:04 +00:00 
			
		
		
		
	 ae66d3aa90
			
		
	
	
		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
 | ||
| }
 |