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