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