xref: /illumos-gate/usr/src/cmd/initpkg/umountall.sh (revision 5bbb4db2c3f208d12bf0fd11769728f9e5ba66a2)
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 2009 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#
173# If we are in deferred activation patching, and the caller is
174# svc.startd, then exit without unmounting any of the remaining
175# file systems since the call path is from shutdown.  Note that
176# by the time we get here, smf stop methods for nfs, cachefs
177# etc, will have run.
178#
179if [ -f $DEFERRED_ACTIVATION_PATCH_FLAG ] ; then
180	ppid=`ps -o ppid= -p $$`	# parent of umountall will be sh
181					# from system()
182
183	ppid=`ps -o ppid= -p $ppid`	# parent of sh will be svc.startd
184	COMM=`ps -o comm= -p $ppid`
185	if [ "$COMM" = "$SVC_STARTD" ] ; then
186		exit
187	fi
188fi
189
190#
191# Take advantage of parallel unmounting at this point if we have no
192# criteria to match and we are in the global zone
193#
194if [ -z "${SFLAG}${LFLAG}${RFLAG}${HFLAG}${KFLAG}${FFLAG}${ZFLAG}" -a \
195    "$ZONENAME" = "global" ]; then
196	umount -a ${UMOUNTFLAG}
197	exit			# with return code of the umount -a
198fi
199
200#
201# Catch uses of /usr commands when /usr is not mounted
202if [ -n "$KFLAG" -a -z "$NFLAG" ]; then
203	if [ ! -x /usr/sbin/fuser ]; then
204		fuser () {
205			echo "umountall: fuser -k skipped (no /usr)" 1>&2
206			# continue - not fatal
207		}
208		sleep () {
209			: # no point in sleeping if fuser is doing nothing
210		}
211	else
212		if [ ! -x /usr/bin/sleep ]; then
213			sleep () {
214				echo "umountall: sleep after fuser -k skipped (no /usr)" 1>&2
215				# continue - not fatal
216			}
217		fi
218	fi
219fi
220
221#
222# Shell function to avoid using /usr/bin/cut.  Given a dev from a
223# fstype=nfs line in mnttab (eg, "host:/export) extract the host
224# component.
225print_host () {
226	OIFS=$IFS
227	IFS=":"
228	set -- $*
229	echo $1
230	IFS=$OIFS
231}
232
233#
234# doumounts echos its return code to stdout, so commands used within
235# this function should take care to produce no other output to stdout.
236doumounts () {
237	(
238	rc=0
239	fslist=""
240	nfslist=""
241	while read dev mountp fstype mode dummy
242	do
243		case "${mountp}" in
244		/			| \
245		/dev			| \
246		/dev/fd			| \
247		/devices		| \
248		/etc/mnttab		| \
249		/etc/svc/volatile	| \
250		/lib			| \
251		/proc			| \
252		/sbin			| \
253		/system/contract	| \
254		/system/object		| \
255		/tmp			| \
256		/tmp/.libgrubmgmt*	| \
257		/usr			| \
258		/var			| \
259		/var/adm		| \
260		/var/run		| \
261		'' )
262			#
263			# file systems possibly mounted in the kernel or
264			# in the methods of some of the file system
265			# services
266			#
267			continue
268			;;
269		* )
270			if [ -n "$HFLAG" ]; then
271				if [ "$fstype" = "nfs" ]; then
272					thishost=`print_host $dev`
273					if [ "$HOST" != "$thishost" ]; then
274						continue
275					fi
276				else
277					continue
278				fi
279			fi
280			if [ -n "$FFLAG" -a "$FSType" != "$fstype" ]; then
281				continue
282			fi
283			if [ -n "$LFLAG" -a "$fstype" = "nfs" ]; then
284				nfslist="$nfslist $mountp"
285				continue
286			fi
287			#
288			# This will filter out autofs mounts with nfs file
289			# system mounted on the top of it.
290			#
291			# WARNING: use of any syscall on a NFS file system has
292			# the danger to go over-the-wire and could cause nfs
293			# clients to hang on shutdown, if the nfs server is
294			# down beforehand.
295			# For the reason described above, a simple test like
296			# "df -F nfs $mountp" can't be used to filter out
297			# nfs-over-autofs mounts. We loop over a list instead:
298			#
299			if [ -n "$LFLAG" -a -n "$nfslist" -a "$fstype" = "autofs" ]
300			then
301				for m in $nfslist; do
302					if [ "$mountp" = "$m" ]; then
303						# Resume the outer while loop
304						continue 2
305					fi
306				done
307			fi
308			if [ -n "$RFLAG" -a "$fstype" != "nfs" ]; then
309				continue
310			fi
311			if [ "$ZONENAME" != "global" ]; then
312				for option in `echo $mode | tr , '\012'`; do
313					#
314					# should not see any zone options
315					# but our own
316					#
317					if [ "$option" = "zone=$ZONENAME" ]; then
318						break
319					fi
320				done
321				if [ "$option" != "zone=$ZONENAME" ]; then
322					continue
323				fi
324			# we are called from the global zone
325			else
326				for option in `echo $mode | tr , '\012'`; do
327					case "$option" in
328					zone=*)
329						option="zone="
330						break
331					;;
332					esac
333				done
334				# skip mounts from non-global zones if ZFLAG is not set
335				if [ "$option" = "zone=" -a -z "$ZFLAG" ]; then
336					continue
337				fi
338				# skip mounts from the global zone if ZFLAG is set
339				if [ "$option" != "zone=" -a -n "$ZFLAG" ]; then
340					continue
341				fi
342			fi
343			if [ -n "${KFLAG}" ]; then
344				fuser -c -k $mountp 1>&2
345				sleep 2
346			fi
347			if [ -n "$SFLAG" ]; then
348				umount ${UMOUNTFLAG} ${mountp} 1>&2
349				trc=$?
350				if [ $trc -ne 0 ]; then
351					rc=$trc
352				fi
353			else
354				# We want to umount in parallel
355				fslist="$fslist $mountp"
356			fi
357		esac
358	done
359
360	if [ -n "$fslist" ]; then
361		umount -a ${UMOUNTFLAG} $fslist 1>&2
362		trc=$?
363		if [ $trc -ne 0 ]; then
364			rc=$trc
365		fi
366	fi
367
368	echo $rc
369	)
370}
371
372#
373# /etc/mnttab has the most recent mounts last.  Reverse it so that we
374# may umount in opposite order to the original mounts.
375#
376
377if [ ! -x /usr/bin/tail ]; then
378	exec < $MNTTAB
379	REVERSED=
380	while read line; do
381		if [ -n "$REVERSED" ]; then
382        		REVERSED="$line\n$REVERSED"
383		else
384			REVERSED="$line"
385		fi
386	done
387
388	error=`echo $REVERSED | doumounts`
389else
390	error=`tail -r $MNTTAB | doumounts`
391fi
392
393exit $error
394