xref: /freebsd/sys/contrib/openzfs/etc/zfs/zfs-functions.in (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
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