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