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