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