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