1# SPDX-License-Identifier: BSD-2-Clause 2# This is a script with common functions etc used by zfs-import, zfs-load-key, 3# zfs-mount, zfs-share and zfs-zed. 4# 5# It is _NOT_ to be called independently 6# 7# Released under the 2-clause BSD license. 8# 9# This script is based on debian/zfsutils.zfs.init from the 10# Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno. 11 12PATH=/sbin:/bin:/usr/bin:/usr/sbin 13 14# Source function library 15if [ -f /etc/rc.d/init.d/functions ]; then 16 # RedHat and derivatives 17 . /etc/rc.d/init.d/functions 18elif [ -L /etc/init.d/functions.sh ]; then 19 # Gentoo 20 . /etc/init.d/functions.sh 21elif [ -f /lib/lsb/init-functions ]; then 22 # LSB, Debian, and derivatives 23 . /lib/lsb/init-functions 24fi 25 26# Of course the functions we need are called differently 27# on different distributions - it would be way too easy 28# otherwise!! 29if type log_failure_msg > /dev/null 2>&1 ; then 30 # LSB functions - fall through 31 zfs_log_begin_msg() { log_begin_msg "$1"; } 32 zfs_log_end_msg() { log_end_msg "$1"; } 33 zfs_log_failure_msg() { log_failure_msg "$1"; } 34 zfs_log_progress_msg() { log_progress_msg "$1"; } 35elif type success > /dev/null 2>&1 ; then 36 # Fedora/RedHat functions 37 zfs_set_ifs() { 38 # For some reason, the init function library have a problem 39 # with a changed IFS, so this function goes around that. 40 local tIFS="$1" 41 if [ -n "$tIFS" ] 42 then 43 TMP_IFS="$IFS" 44 IFS="$tIFS" 45 fi 46 } 47 48 zfs_log_begin_msg() { printf "%s" "$1 "; } 49 zfs_log_end_msg() { 50 # shellcheck disable=SC2154 51 zfs_set_ifs "$OLD_IFS" 52 if [ "$1" -eq 0 ]; then 53 success 54 else 55 failure 56 fi 57 echo 58 zfs_set_ifs "$TMP_IFS" 59 } 60 zfs_log_failure_msg() { 61 zfs_set_ifs "$OLD_IFS" 62 failure 63 echo 64 zfs_set_ifs "$TMP_IFS" 65 } 66 zfs_log_progress_msg() { printf "%s" "$""$1"; } 67elif type einfo > /dev/null 2>&1 ; then 68 # Gentoo functions 69 zfs_log_begin_msg() { ebegin "$1"; } 70 zfs_log_end_msg() { eend "$1"; } 71 zfs_log_failure_msg() { eend "$1"; } 72# zfs_log_progress_msg() { printf "%s" "$1"; } 73 zfs_log_progress_msg() { :; } 74else 75 # Unknown - simple substitutes. 76 zfs_log_begin_msg() { printf "%s" "$1"; } 77 zfs_log_end_msg() { 78 ret=$1 79 if [ "$ret" -ge 1 ]; then 80 echo " failed!" 81 else 82 echo " success" 83 fi 84 return "$ret" 85 } 86 zfs_log_failure_msg() { echo "$1"; } 87 zfs_log_progress_msg() { printf "%s" "$1"; } 88fi 89 90# Paths to what we need 91ZFS="@sbindir@/zfs" 92ZED="@sbindir@/zed" 93ZPOOL="@sbindir@/zpool" 94ZPOOL_CACHE="@sysconfdir@/zfs/zpool.cache" 95 96# Sensible defaults 97ZFS_LOAD_KEY='yes' 98ZFS_UNLOAD_KEY='no' 99ZFS_MOUNT='yes' 100ZFS_UNMOUNT='yes' 101ZFS_SHARE='yes' 102ZFS_UNSHARE='yes' 103 104# Source zfs configuration, overriding the defaults 105if [ -f @initconfdir@/zfs ]; then 106 . @initconfdir@/zfs 107fi 108 109# ---------------------------------------------------- 110 111export ZFS ZED ZPOOL ZPOOL_CACHE ZFS_LOAD_KEY ZFS_UNLOAD_KEY ZFS_MOUNT ZFS_UNMOUNT \ 112 ZFS_SHARE ZFS_UNSHARE 113 114zfs_action() 115{ 116 local MSG="$1"; shift 117 local CMD="$*" 118 local ret 119 120 zfs_log_begin_msg "$MSG " 121 $CMD 122 ret=$? 123 if [ "$ret" -eq 0 ]; then 124 zfs_log_end_msg "$ret" 125 else 126 zfs_log_failure_msg "$ret" 127 fi 128 129 return "$ret" 130} 131 132# Returns 133# 0 if daemon has been started 134# 1 if daemon was already running 135# 2 if daemon could not be started 136# 3 if unsupported 137# 138zfs_daemon_start() 139{ 140 local PIDFILE="$1"; shift 141 local DAEMON_BIN="$1"; shift 142 143 if type start-stop-daemon > /dev/null 2>&1 ; then 144 # LSB functions 145 start-stop-daemon --start --quiet --pidfile "$PIDFILE" \ 146 --exec "$DAEMON_BIN" --test > /dev/null || return 1 147 148 # shellcheck disable=SC2086 149 start-stop-daemon --start --quiet --exec "$DAEMON_BIN" -- \ 150 "$@" || return 2 151 152 # On Debian, there's a 'sendsigs' script that will 153 # kill basically everything quite early and zed is stopped 154 # much later than that. We don't want zed to be among them, 155 # so add the zed pid to list of pids to ignore. 156 if [ -f "$PIDFILE" ] && [ -d /run/sendsigs.omit.d ] 157 then 158 ln -sf "$PIDFILE" /run/sendsigs.omit.d/zed 159 fi 160 elif type daemon > /dev/null 2>&1 ; then 161 # Fedora/RedHat functions 162 # shellcheck disable=SC2086 163 daemon --pidfile "$PIDFILE" "$DAEMON_BIN" "$@" 164 return $? 165 else 166 # Unsupported 167 return 3 168 fi 169 170 return 0 171} 172 173# Returns 174# 0 if daemon has been stopped 175# 1 if daemon was already stopped 176# 2 if daemon could not be stopped 177# 3 if unsupported 178# 179zfs_daemon_stop() 180{ 181 local PIDFILE="$1" 182 local DAEMON_BIN="$2" 183 local DAEMON_NAME="$3" 184 185 if type start-stop-daemon > /dev/null 2>&1 ; then 186 # LSB functions 187 start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 \ 188 --pidfile "$PIDFILE" --name "$DAEMON_NAME" 189 ret="$?" 190 [ "$ret" = 0 ] && rm -f "$PIDFILE" 191 192 return "$ret" 193 elif type killproc > /dev/null 2>&1 ; then 194 # Fedora/RedHat functions 195 killproc -p "$PIDFILE" "$DAEMON_NAME" 196 ret="$?" 197 [ "$ret" = 0 ] && rm -f "$PIDFILE" 198 199 return "$ret" 200 else 201 # Unsupported 202 return 3 203 fi 204 205 return 0 206} 207 208# Returns status 209zfs_daemon_status() 210{ 211 local PIDFILE="$1" 212 local DAEMON_BIN="$2" 213 local DAEMON_NAME="$3" 214 215 if type status_of_proc > /dev/null 2>&1 ; then 216 # LSB functions 217 status_of_proc "$DAEMON_NAME" "$DAEMON_BIN" 218 return $? 219 elif type status > /dev/null 2>&1 ; then 220 # Fedora/RedHat functions 221 status -p "$PIDFILE" "$DAEMON_NAME" 222 return $? 223 else 224 # Unsupported 225 return 3 226 fi 227 228 return 0 229} 230 231zfs_daemon_reload() 232{ 233 local PIDFILE="$1" 234 local DAEMON_NAME="$2" 235 236 if type start-stop-daemon > /dev/null 2>&1 ; then 237 # LSB functions 238 start-stop-daemon --stop --signal 1 --quiet \ 239 --pidfile "$PIDFILE" --name "$DAEMON_NAME" 240 return $? 241 elif type killproc > /dev/null 2>&1 ; then 242 # Fedora/RedHat functions 243 killproc -p "$PIDFILE" "$DAEMON_NAME" -HUP 244 return $? 245 else 246 # Unsupported 247 return 3 248 fi 249 250 return 0 251} 252 253zfs_installed() 254{ 255 if [ ! -x "$ZPOOL" ]; then 256 return 1 257 else 258 # Test if it works (will catch missing/broken libs etc) 259 "$ZPOOL" -? > /dev/null 2>&1 260 return $? 261 fi 262 263 if [ ! -x "$ZFS" ]; then 264 return 2 265 else 266 # Test if it works (will catch missing/broken libs etc) 267 "$ZFS" -? > /dev/null 2>&1 268 return $? 269 fi 270 271 return 0 272} 273 274# Trigger udev and wait for it to settle. 275udev_trigger() 276{ 277 if [ -x /sbin/udevadm ]; then 278 /sbin/udevadm trigger --action=change --subsystem-match=block 279 /sbin/udevadm settle 280 elif [ -x /sbin/udevsettle ]; then 281 /sbin/udevtrigger 282 /sbin/udevsettle 283 fi 284} 285 286# Do a lot of checks to make sure it's 'safe' to continue with the import. 287checksystem() 288{ 289 if grep -qiE '(^|[^\\](\\\\)* )zfs=(off|no|0)( |$)' /proc/cmdline; 290 then 291 # Called with zfs=(off|no|0) - bail because we don't 292 # want anything import, mounted or shared. 293 # HOWEVER, only do this if we're called at the boot up 294 # (from init), not if we're running interactively (as in 295 # from the shell - we know what we're doing). 296 # shellcheck disable=SC2154 297 [ -n "$init" ] && exit 3 298 fi 299 300 # Check if ZFS is installed. 301 zfs_installed || return 5 302 303 # Just make sure that /dev/zfs is created. 304 udev_trigger 305 306 return 0 307} 308 309get_root_pool() 310{ 311 # shellcheck disable=SC2046 312 set -- $(mount | grep ' on / ') 313 [ "$5" = "zfs" ] && echo "${1%%/*}" 314} 315 316# Check if a variable is 'yes' (any case) or '1' 317# Returns TRUE if set. 318check_boolean() 319{ 320 local var="$1" 321 322 echo "$var" | grep -Eiq "^yes$|^on$|^true$|^1$" && return 0 || return 1 323} 324 325check_module_loaded() 326{ 327 module="$1" 328 329 [ -r "/sys/module/${module}/version" ] && return 0 || return 1 330} 331 332load_module() 333{ 334 module="$1" 335 336 # Load the zfs module stack 337 if ! check_module_loaded "$module"; then 338 if ! /sbin/modprobe "$module"; then 339 return 5 340 fi 341 fi 342 return 0 343} 344 345# first parameter is a regular expression that filters mtab 346read_mtab() 347{ 348 local match="$1" 349 local fs mntpnt fstype opts rest 350 351 # Unset all MTAB_* variables 352 # shellcheck disable=SC2046 353 unset $(env | sed -e '/^MTAB_/!d' -e 's,=.*,,') 354 355 while read -r fs mntpnt fstype opts rest; do 356 if echo "$fs $mntpnt $fstype $opts" | grep -qE "$match"; then 357 # * Fix problems (!?) in the mounts file. It will record 358 # 'rpool 1' as 'rpool\0401' instead of 'rpool\00401' 359 # which seems to be the correct (at least as far as 360 # 'printf' is concerned). 361 # * We need to use the external echo, because the 362 # internal one would interpret the backslash code 363 # (incorrectly), giving us a instead. 364 mntpnt=$(/bin/echo "$mntpnt" | sed 's,\\0,\\00,g') 365 fs=$(/bin/echo "$fs" | sed 's,\\0,\\00,') 366 367 # Remove 'unwanted' characters. 368 mntpnt=$(printf '%b' "$mntpnt" | tr -d '/. -') 369 fs=$(printf '%b' "$fs") 370 371 # Set the variable. 372 eval export "MTAB_$mntpnt=\"$fs\"" 373 fi 374 done < /proc/self/mounts 375} 376 377in_mtab() 378{ 379 local mntpnt="$1" 380 # Remove 'unwanted' characters. 381 mntpnt=$(printf '%b' "$mntpnt" | tr -d '/. -') 382 local var 383 384 var="$(eval echo "MTAB_$mntpnt")" 385 [ "$(eval echo "$""$var")" != "" ] 386 return "$?" 387} 388 389# first parameter is a regular expression that filters fstab 390read_fstab() 391{ 392 local match="$1" 393 local i var 394 395 # Unset all FSTAB_* variables 396 # shellcheck disable=SC2046 397 unset $(env | sed -e '/^FSTAB_/!d' -e 's,=.*,,') 398 399 i=0 400 while read -r fs mntpnt fstype opts; do 401 echo "$fs" | grep -qE '^#|^$' && continue 402 echo "$mntpnt" | grep -qE '^none|^swap' && continue 403 echo "$fstype" | grep -qE '^swap' && continue 404 405 if echo "$fs $mntpnt $fstype $opts" | grep -qE "$match"; then 406 eval export "FSTAB_dev_$i=$fs" 407 fs=$(printf '%b' "$fs" | tr '/' '_') 408 eval export "FSTAB_$i=$mntpnt" 409 410 i=$((i + 1)) 411 fi 412 done < /etc/fstab 413} 414 415in_fstab() 416{ 417 local var 418 419 var="$(eval echo "FSTAB_$1")" 420 [ "${var}" != "" ] 421 return $? 422} 423 424is_mounted() 425{ 426 local mntpt="$1" 427 local mp 428 429 while read -r _ mp _; do 430 [ "$mp" = "$mntpt" ] && return 0 431 done < /proc/self/mounts 432 433 return 1 434} 435