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