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