xref: /illumos-gate/usr/src/cmd/initpkg/umountall.sh (revision bfed486ad8de8b8ebc6345a8e10accae08bf2f45)
1#!/sbin/sh
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License (the "License").
7# You may not use this file except in compliance with the License.
8#
9# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10# or http://www.opensolaris.org/os/licensing.
11# See the License for the specific language governing permissions
12# and limitations under the License.
13#
14# When distributing Covered Code, include this CDDL HEADER in each
15# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16# If applicable, add the following below this CDDL HEADER, with the
17# fields enclosed by brackets "[]" replaced with your own identifying
18# information: Portions Copyright [yyyy] [name of copyright owner]
19#
20# CDDL HEADER END
21#
22#
23# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24# Use is subject to license terms.
25#
26#	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T
27#	  All Rights Reserved
28#
29#
30
31usage () {
32	if [ -n "$1" ]; then
33		echo "umountall: $1" 1>&2
34	fi
35	echo "Usage:\n\tumountall [-k] [-s] [-F FSType] [-l|-r] [-Z] [-n]" 1>&2
36	echo "\tumountall [-k] [-s] [-h host] [-Z] [-n]" 1>&2
37	exit 2
38}
39
40MNTTAB=/etc/mnttab
41
42# This script is installed as both /sbin/umountall (as used in some
43# /sbin/rc? and /etc/init.d scripts) _and_ as /usr/sbin/umountall (typically
44# PATHed from the command line).  As such it should not depend on /usr
45# being mounted (if /usr is a separate filesystem).
46#
47# /sbin/sh Bourne shell builtins we use:
48#	echo
49#	exit
50#	getopts
51#	test, [ ]
52#	exec
53#	read
54#
55# /sbin commands we use:
56#	/sbin/uname
57#	/sbin/umount
58#
59# The following /usr based commands may be used by this script (depending on
60# command line options).  We will set our PATH to find them, but where they
61# are not present (eg, if /usr is not mounted) we will catch references to
62# them via shell functions conditionally defined after option processing
63# (don't use any of these commands before then).
64#
65#	Command		Command line option and use
66# /usr/bin/sleep	-k, to sleep after an fuser -c -k on the mountpoint
67# /usr/sbin/fuser	-k, to kill processes keeping a mount point busy
68#
69# In addition, we use /usr/bin/tail if it is available; if not we use
70# slower shell constructs to reverse a file.
71
72PATH=/sbin:/usr/sbin:/usr/bin
73
74DEFERRED_ACTIVATION_PATCH_FLAG="/var/run/.patch_loopback_mode"
75SVC_STARTD="/lib/svc/bin/svc.startd"
76
77# Clear these in case they were already set in our inherited environment.
78FSType=
79FFLAG=
80HOST=
81HFLAG=
82RFLAG=
83LFLAG=
84SFLAG=
85KFLAG=
86ZFLAG=
87NFLAG=
88LOCALNAME=
89UMOUNTFLAG=
90
91
92while getopts ?rslkF:h:Zn c
93do
94	case $c in
95	r)	RFLAG="r";;
96	l)	LFLAG="l";;
97	s)	SFLAG="s";;
98	k)	KFLAG="k";;
99	h)	if [ -n "$HFLAG" ]; then
100			usage "more than one host specified"
101		fi
102		HOST=$OPTARG
103		HFLAG="h"
104		LOCALNAME=`uname -n`
105		;;
106	F)	if [ -n "$FFLAG" ]; then
107			usage "more than one FStype specified"
108		fi
109		FSType=$OPTARG
110		FFLAG="f"
111		case $FSType in
112		?????????*)
113			usage "FSType ${FSType} exceeds 8 characters"
114		esac;
115		;;
116	Z)	ZFLAG="z";;
117	n)	NFLAG="n"
118		# Alias any commands that would perform real actions to
119		# something that tells what action would have been performed
120		UMOUNTFLAG="-V"
121		fuser () {
122			echo "fuser $*" 1>&2
123		}
124		sleep () {
125			: # No need to show where we'd sleep
126		}
127		;;
128	\?)	usage ""
129		;;
130	esac
131done
132
133# Sanity checking:
134#	1) arguments beyond those supported
135#	2) can't specify both remote and local
136#	3) can't specify a host with -r or -l
137#	4) can't specify a fstype with -h
138#	5) can't specify this host with -h (checks only uname -n)
139#	6) can't be fstype nfs and local
140#	7) only fstype nfs is remote
141
142if [ $# -ge $OPTIND ]; then						# 1
143	usage "additional arguments not supported"
144fi
145
146if [ -n "$RFLAG" -a -n "$LFLAG" ]; then					# 2
147	usage "options -r and -l are incompatible"
148fi
149
150if [ \( -n "$RFLAG" -o -n "$LFLAG" \) -a "$HFLAG" = "h" ]; then		# 3
151	usage "option -${RFLAG}${LFLAG} incompatible with -h option"
152fi
153
154if [ -n "$FFLAG" -a "$HFLAG" = "h" ]; then				# 4
155	usage "Specifying FStype incompatible with -h option"
156fi
157
158if [ -n "$HFLAG" -a "$HOST" = "$LOCALNAME" ]; then			# 5
159	usage "Specifying local host illegal for -h option"
160fi
161
162if [ "$FSType" = "nfs" -a "$LFLAG" = "l" ]; then			# 6
163	usage "option -l and FSType nfs are incompatible"
164fi
165
166if [ -n "$FFLAG" -a "$FSType" != "nfs"  -a -n "$RFLAG" ]; then		# 7
167	usage "option -r and FSType ${FSType} are incompatible"
168fi
169
170ZONENAME=`zonename`
171
172# Check and if needed sync the boot archive before unmounting everything.
173#
174if [ -z "${RFLAG}${NFLAG}${HFLAG}${FSType}" -a "$ZONENAME" = "global" \
175    -a -x /sbin/bootadm ] ; then
176	/sbin/bootadm -a update_all
177fi
178
179
180#
181# If we are in deferred activation patching, and the caller is
182# svc.startd, then exit without unmounting any of the remaining
183# file systems since the call path is from shutdown.  Note that
184# by the time we get here, smf stop methods for nfs, cachefs
185# etc, will have run.
186#
187if [ -f $DEFERRED_ACTIVATION_PATCH_FLAG ] ; then
188	ppid=`ps -o ppid= -p $$`	# parent of umountall will be sh
189					# from system()
190
191	ppid=`ps -o ppid= -p $ppid`	# parent of sh will be svc.startd
192	COMM=`ps -o comm= -p $ppid`
193	if [ "$COMM" = "$SVC_STARTD" ] ; then
194		exit
195	fi
196fi
197
198#
199# Take advantage of parallel unmounting at this point if we have no
200# criteria to match and we are in the global zone
201#
202if [ -z "${SFLAG}${LFLAG}${RFLAG}${HFLAG}${KFLAG}${FFLAG}${ZFLAG}" -a \
203    "$ZONENAME" = "global" ]; then
204	umount -a ${UMOUNTFLAG}
205	exit			# with return code of the umount -a
206fi
207
208#
209# Catch uses of /usr commands when /usr is not mounted
210if [ -n "$KFLAG" -a -z "$NFLAG" ]; then
211	if [ ! -x /usr/sbin/fuser ]; then
212		fuser () {
213			echo "umountall: fuser -k skipped (no /usr)" 1>&2
214			# continue - not fatal
215		}
216		sleep () {
217			: # no point in sleeping if fuser is doing nothing
218		}
219	else
220		if [ ! -x /usr/bin/sleep ]; then
221			sleep () {
222				echo "umountall: sleep after fuser -k skipped (no /usr)" 1>&2
223				# continue - not fatal
224			}
225		fi
226	fi
227fi
228
229#
230# Shell function to avoid using /usr/bin/cut.  Given a dev from a
231# fstype=nfs line in mnttab (eg, "host:/export) extract the host
232# component.
233print_host () {
234	OIFS=$IFS
235	IFS=":"
236	set -- $*
237	echo $1
238	IFS=$OIFS
239}
240
241#
242# doumounts echos its return code to stdout, so commands used within
243# this function should take care to produce no other output to stdout.
244doumounts () {
245	(
246	rc=0
247	fslist=""
248	nfslist=""
249	while read dev mountp fstype mode dummy
250	do
251		case "${mountp}" in
252		/			| \
253		/dev			| \
254		/dev/fd			| \
255		/devices		| \
256		/etc/mnttab		| \
257		/etc/svc/volatile	| \
258		/lib			| \
259		/proc			| \
260		/sbin			| \
261		/system/contract	| \
262		/system/object		| \
263		/tmp			| \
264		/usr			| \
265		/var			| \
266		/var/adm		| \
267		/var/run		| \
268		'' )
269			#
270			# file systems possibly mounted in the kernel or
271			# in the methods of some of the file system
272			# services
273			#
274			continue
275			;;
276		* )
277			if [ -n "$HFLAG" ]; then
278				if [ "$fstype" = "nfs" ]; then
279					thishost=`print_host $dev`
280					if [ "$HOST" != "$thishost" ]; then
281						continue
282					fi
283				else
284					continue
285				fi
286			fi
287			if [ -n "$FFLAG" -a "$FSType" != "$fstype" ]; then
288				continue
289			fi
290			if [ -n "$LFLAG" -a "$fstype" = "nfs" ]; then
291				nfslist="$nfslist $mountp"
292				continue
293			fi
294			#
295			# This will filter out autofs mounts with nfs file
296			# system mounted on the top of it.
297			#
298			# WARNING: use of any syscall on a NFS file system has
299			# the danger to go over-the-wire and could cause nfs
300			# clients to hang on shutdown, if the nfs server is
301			# down beforehand.
302			# For the reason described above, a simple test like
303			# "df -F nfs $mountp" can't be used to filter out
304			# nfs-over-autofs mounts. We loop over a list instead:
305			#
306			if [ -n "$LFLAG" -a -n "$nfslist" -a "$fstype" = "autofs" ]
307			then
308				for m in $nfslist; do
309					if [ "$mountp" = "$m" ]; then
310						# Resume the outer while loop
311						continue 2
312					fi
313				done
314			fi
315			if [ -n "$RFLAG" -a "$fstype" != "nfs" ]; then
316				continue
317			fi
318			if [ "$ZONENAME" != "global" ]; then
319				for option in `echo $mode | tr , '\012'`; do
320					#
321					# should not see any zone options
322					# but our own
323					#
324					if [ "$option" = "zone=$ZONENAME" ]; then
325						break
326					fi
327				done
328				if [ "$option" != "zone=$ZONENAME" ]; then
329					continue
330				fi
331			# we are called from the global zone
332			else
333				for option in `echo $mode | tr , '\012'`; do
334					case "$option" in
335					zone=*)
336						option="zone="
337						break
338					;;
339					esac
340				done
341				# skip mounts from non-global zones if ZFLAG is not set
342				if [ "$option" = "zone=" -a -z "$ZFLAG" ]; then
343					continue
344				fi
345				# skip mounts from the global zone if ZFLAG is set
346				if [ "$option" != "zone=" -a -n "$ZFLAG" ]; then
347					continue
348				fi
349			fi
350			if [ -n "${KFLAG}" ]; then
351				fuser -c -k $mountp 1>&2
352				sleep 2
353			fi
354			if [ -n "$SFLAG" ]; then
355				umount ${UMOUNTFLAG} ${mountp} 1>&2
356				trc=$?
357				if [ $trc -ne 0 ]; then
358					rc=$trc
359				fi
360			else
361				# We want to umount in parallel
362				fslist="$fslist $mountp"
363			fi
364		esac
365	done
366
367	if [ -n "$fslist" ]; then
368		umount -a ${UMOUNTFLAG} $fslist 1>&2
369		trc=$?
370		if [ $trc -ne 0 ]; then
371			rc=$trc
372		fi
373	fi
374
375	echo $rc
376	)
377}
378
379#
380# /etc/mnttab has the most recent mounts last.  Reverse it so that we
381# may umount in opposite order to the original mounts.
382#
383
384if [ ! -x /usr/bin/tail ]; then
385	exec < $MNTTAB
386	REVERSED=
387	while read line; do
388		if [ -n "$REVERSED" ]; then
389        		REVERSED="$line\n$REVERSED"
390		else
391			REVERSED="$line"
392		fi
393	done
394
395	error=`echo $REVERSED | doumounts`
396else
397	error=`tail -r $MNTTAB | doumounts`
398fi
399
400exit $error
401