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