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