1#!@DEFAULT_INIT_SHELL@
2#
3# zfs-import    This script will import ZFS pools
4#
5# chkconfig:    2345 01 99
6# description:  This script will perform a verbatim import of ZFS pools
7#               during system boot.
8# probe: true
9#
10### BEGIN INIT INFO
11# Provides:          zfs-import
12# Required-Start:    mtab
13# Required-Stop:     $local_fs mtab
14# Default-Start:     S
15# Default-Stop:      0 1 6
16# X-Start-Before:    checkfs
17# X-Stop-After:      zfs-mount
18# Short-Description: Import ZFS pools
19# Description: Run the `zpool import` command.
20### END INIT INFO
21#
22# NOTE: Not having '$local_fs' on Required-Start but only on Required-Stop
23#       is on purpose. If we have '$local_fs' in both (and X-Start-Before=checkfs)
24#       we get conflicts - import needs to be started extremely early,
25#       but not stopped too late.
26#
27# Released under the 2-clause BSD license.
28#
29# This script is based on debian/zfsutils.zfs.init from the
30# Debian GNU/kFreeBSD zfsutils 8.1-3 package, written by Aurelien Jarno.
31
32# Source the common init script
33. @sysconfdir@/zfs/zfs-functions
34
35# ----------------------------------------------------
36
37do_depend()
38{
39	before swap
40	after sysfs udev
41	keyword -lxc -openvz -prefix -vserver
42}
43
44# Use the zpool cache file to import pools
45do_verbatim_import()
46{
47	if [ -f "$ZPOOL_CACHE" ]
48	then
49		zfs_action "Importing ZFS pool(s)" \
50			"$ZPOOL" import -c "$ZPOOL_CACHE" -N -a
51	fi
52}
53
54# Support function to get a list of all pools, separated with ';'
55find_pools()
56{
57	local pools
58
59	pools=$("$@" 2> /dev/null | \
60		sed -Ee '/pool:|^[a-zA-Z0-9]/!d' -e 's@.*: @@' | \
61		sort | \
62		tr '\n' ';')
63
64	echo "${pools%%;}" # Return without the last ';'.
65}
66
67# Find and import all visible pools, even exported ones
68do_import_all_visible()
69{
70	local already_imported available_pools pool npools
71	local exception dir ZPOOL_IMPORT_PATH RET=0 r=1
72
73	# In case not shutdown cleanly.
74	# shellcheck disable=SC2154
75	[ -n "$init" ] && rm -f /etc/dfs/sharetab
76
77	# Just simplify code later on.
78	if [ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ]
79	then
80		# It's something, but not 'yes' so it's no good to us.
81		unset USE_DISK_BY_ID
82	fi
83
84	# Find list of already imported pools.
85	already_imported=$(find_pools "$ZPOOL" list -H -oname)
86	available_pools=$(find_pools "$ZPOOL" import)
87
88	# Just in case - seen it happen (that a pool isn't visible/found
89	# with a simple "zpool import" but only when using the "-d"
90	# option or setting ZPOOL_IMPORT_PATH).
91	if [ -d "/dev/disk/by-id" ]
92	then
93		npools=$(find_pools "$ZPOOL" import -d /dev/disk/by-id)
94		if [ -n "$npools" ]
95		then
96			# Because we have found extra pool(s) here, which wasn't
97			# found 'normally', we need to force USE_DISK_BY_ID to
98			# make sure we're able to actually import it/them later.
99			USE_DISK_BY_ID='yes'
100
101			if [ -n "$available_pools" ]
102			then
103				# Filter out duplicates (pools found with the simpl
104				# "zpool import" but which is also found with the
105				# "zpool import -d ...").
106				npools=$(echo "$npools" | sed "s,$available_pools,,")
107
108				# Add the list to the existing list of
109				# available pools
110				available_pools="$available_pools;$npools"
111			else
112				available_pools="$npools"
113			fi
114		fi
115	fi
116
117	# Filter out any exceptions...
118	if [ -n "$ZFS_POOL_EXCEPTIONS" ]
119	then
120		local found=""
121		local apools=""
122		OLD_IFS="$IFS" ; IFS=";"
123
124		for pool in $available_pools
125		do
126			for exception in $ZFS_POOL_EXCEPTIONS
127			do
128				[ "$pool" = "$exception" ] && continue 2
129				found="$pool"
130			done
131
132			if [ -n "$found" ]
133			then
134				if [ -n "$apools" ]
135				then
136					apools="$apools;$pool"
137				else
138					apools="$pool"
139				fi
140			fi
141		done
142
143		IFS="$OLD_IFS"
144		available_pools="$apools"
145	fi
146
147	# For backwards compatibility, make sure that ZPOOL_IMPORT_PATH is set
148	# to something we can use later with the real import(s). We want to
149	# make sure we find all by* dirs, BUT by-vdev should be first (if it
150	# exists).
151	if [ -n "$USE_DISK_BY_ID" ] && [ -z "$ZPOOL_IMPORT_PATH" ]
152	then
153		local dirs
154		dirs="$(for dir in $(echo /dev/disk/by-*)
155		do
156			# Ignore by-vdev here - we want it first!
157			echo "$dir" | grep -q /by-vdev && continue
158			[ ! -d "$dir" ] && continue
159
160			printf "%s" "$dir:"
161		done | sed 's,:$,,g')"
162
163		if [ -d "/dev/disk/by-vdev" ]
164		then
165			# Add by-vdev at the beginning.
166			ZPOOL_IMPORT_PATH="/dev/disk/by-vdev:"
167		fi
168
169		# Help with getting LUKS partitions etc imported.
170		if [ -d "/dev/mapper" ]; then
171			if [ -n "$ZPOOL_IMPORT_PATH" ]; then
172				ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH:/dev/mapper:"
173			else
174				ZPOOL_IMPORT_PATH="/dev/mapper:"
175			fi
176		fi
177
178		# ... and /dev at the very end, just for good measure.
179		ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH$dirs:/dev"
180	fi
181
182	# Needs to be exported for "zpool" to catch it.
183	[ -n "$ZPOOL_IMPORT_PATH" ] && export ZPOOL_IMPORT_PATH
184
185	# Mount all available pools (except those set in ZFS_POOL_EXCEPTIONS.
186	#
187	# If not interactive (run from init - variable init='/sbin/init')
188	# we get ONE line for all pools being imported, with just a dot
189	# as status for each pool.
190	# Example: Importing ZFS pool(s)...                             [OK]
191	#
192	# If it IS interactive (started from the shell manually), then we
193	# get one line per pool importing.
194	# Example: Importing ZFS pool pool1                             [OK]
195	#          Importing ZFS pool pool2                             [OK]
196	#          [etc]
197	[ -n "$init" ] && zfs_log_begin_msg "Importing ZFS pool(s)"
198	OLD_IFS="$IFS" ; IFS=";"
199	for pool in $available_pools
200	do
201		[ -z "$pool" ] && continue
202
203		# We have pools that haven't been imported - import them
204		if [ -n "$init" ]
205		then
206			# Not interactive - a dot for each pool.
207			# Except on Gentoo where this doesn't work.
208			zfs_log_progress_msg "."
209		else
210			# Interactive - one 'Importing ...' line per pool
211			zfs_log_begin_msg "Importing ZFS pool $pool"
212		fi
213
214		# Import by using ZPOOL_IMPORT_PATH (either set above or in
215		# the config file) _or_ with the 'built in' default search
216		# paths. This is the preferred way.
217		# shellcheck disable=SC2086
218		"$ZPOOL" import -N ${ZPOOL_IMPORT_OPTS} "$pool" 2> /dev/null
219		r="$?" ; RET=$((RET + r))
220		if [ "$r" -eq 0 ]
221		then
222			# Output success and process the next pool
223			[ -z "$init" ] && zfs_log_end_msg 0
224			continue
225		fi
226		# We don't want a fail msg here, we're going to try import
227		# using the cache file soon and that might succeed.
228		[ ! -f "$ZPOOL_CACHE" ] && zfs_log_end_msg "$RET"
229
230		if [ "$r" -gt 0 ] && [ -f "$ZPOOL_CACHE" ]
231		then
232			# Failed to import without a cache file. Try WITH...
233			if [ -z "$init" ] && check_boolean "$VERBOSE_MOUNT"
234			then
235				# Interactive + Verbose = more information
236				zfs_log_progress_msg " using cache file"
237			fi
238
239			# shellcheck disable=SC2086
240			"$ZPOOL" import -c "$ZPOOL_CACHE" -N ${ZPOOL_IMPORT_OPTS} \
241				"$pool" 2> /dev/null
242			r="$?" ; RET=$((RET + r))
243			if [ "$r" -eq 0 ]
244			then
245				[ -z "$init" ] && zfs_log_end_msg 0
246				continue 3 # Next pool
247			fi
248			zfs_log_end_msg "$RET"
249		fi
250	done
251	[ -n "$init" ] && zfs_log_end_msg "$RET"
252
253	IFS="$OLD_IFS"
254	[ -n "$already_imported" ] && [ -z "$available_pools" ] && return 0
255
256	return "$RET"
257}
258
259do_import()
260{
261	if check_boolean "$ZPOOL_IMPORT_ALL_VISIBLE"
262	then
263		do_import_all_visible
264	else
265		# This is the default option
266		do_verbatim_import
267	fi
268}
269
270# Output the status and list of pools
271do_status()
272{
273	check_module_loaded "zfs" || exit 0
274
275	"$ZPOOL" status && echo "" && "$ZPOOL" list
276}
277
278do_start()
279{
280	if check_boolean "$VERBOSE_MOUNT"
281	then
282	    zfs_log_begin_msg "Checking if ZFS userspace tools present"
283	fi
284
285	if checksystem
286	then
287		check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 0
288
289		check_boolean "$VERBOSE_MOUNT" && \
290			zfs_log_begin_msg "Loading kernel ZFS infrastructure"
291
292		if ! load_module "zfs"
293		then
294			check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 1
295			return 5
296		fi
297		check_boolean "$VERBOSE_MOUNT" && zfs_log_end_msg 0
298
299		do_import && udev_trigger # just to make sure we get zvols.
300
301		return 0
302	else
303		return 1
304	fi
305}
306
307# ----------------------------------------------------
308
309if [ ! -e /sbin/openrc-run ]
310then
311	case "$1" in
312		start)
313			do_start
314			;;
315		stop)
316			# no-op
317			;;
318		status)
319			do_status
320			;;
321		force-reload|condrestart|reload|restart)
322			# no-op
323			;;
324		*)
325			[ -n "$1" ] && echo "Error: Unknown command $1."
326			echo "Usage: $0 {start|status}"
327			exit 3
328			;;
329	esac
330
331	exit $?
332else
333	# Create wrapper functions since Gentoo don't use the case part.
334	depend() { do_depend; }
335	start() { do_start; }
336	status() { do_status; }
337fi
338