xref: /illumos-gate/usr/src/cmd/initpkg/umountall.sh (revision c211fc479225fa54805cf480633bf6689ca9a2db)
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		/usr			| \
257		/var			| \
258		/var/adm		| \
259		/var/run		| \
260		'' )
261			#
262			# file systems possibly mounted in the kernel or
263			# in the methods of some of the file system
264			# services
265			#
266			continue
267			;;
268		* )
269			if [ -n "$HFLAG" ]; then
270				if [ "$fstype" = "nfs" ]; then
271					thishost=`print_host $dev`
272					if [ "$HOST" != "$thishost" ]; then
273						continue
274					fi
275				else
276					continue
277				fi
278			fi
279			if [ -n "$FFLAG" -a "$FSType" != "$fstype" ]; then
280				continue
281			fi
282			if [ -n "$LFLAG" -a "$fstype" = "nfs" ]; then
283				nfslist="$nfslist $mountp"
284				continue
285			fi
286			#
287			# This will filter out autofs mounts with nfs file
288			# system mounted on the top of it.
289			#
290			# WARNING: use of any syscall on a NFS file system has
291			# the danger to go over-the-wire and could cause nfs
292			# clients to hang on shutdown, if the nfs server is
293			# down beforehand.
294			# For the reason described above, a simple test like
295			# "df -F nfs $mountp" can't be used to filter out
296			# nfs-over-autofs mounts. We loop over a list instead:
297			#
298			if [ -n "$LFLAG" -a -n "$nfslist" -a "$fstype" = "autofs" ]
299			then
300				for m in $nfslist; do
301					if [ "$mountp" = "$m" ]; then
302						# Resume the outer while loop
303						continue 2
304					fi
305				done
306			fi
307			if [ -n "$RFLAG" -a "$fstype" != "nfs" ]; then
308				continue
309			fi
310			if [ "$ZONENAME" != "global" ]; then
311				for option in `echo $mode | tr , '\012'`; do
312					#
313					# should not see any zone options
314					# but our own
315					#
316					if [ "$option" = "zone=$ZONENAME" ]; then
317						break
318					fi
319				done
320				if [ "$option" != "zone=$ZONENAME" ]; then
321					continue
322				fi
323			# we are called from the global zone
324			else
325				for option in `echo $mode | tr , '\012'`; do
326					case "$option" in
327					zone=*)
328						option="zone="
329						break
330					;;
331					esac
332				done
333				# skip mounts from non-global zones if ZFLAG is not set
334				if [ "$option" = "zone=" -a -z "$ZFLAG" ]; then
335					continue
336				fi
337				# skip mounts from the global zone if ZFLAG is set
338				if [ "$option" != "zone=" -a -n "$ZFLAG" ]; then
339					continue
340				fi
341			fi
342			if [ -n "${KFLAG}" ]; then
343				fuser -c -k $mountp 1>&2
344				sleep 2
345			fi
346			if [ -n "$SFLAG" ]; then
347				umount ${UMOUNTFLAG} ${mountp} 1>&2
348				trc=$?
349				if [ $trc -ne 0 ]; then
350					rc=$trc
351				fi
352			else
353				# We want to umount in parallel
354				fslist="$fslist $mountp"
355			fi
356		esac
357	done
358
359	if [ -n "$fslist" ]; then
360		umount -a ${UMOUNTFLAG} $fslist 1>&2
361		trc=$?
362		if [ $trc -ne 0 ]; then
363			rc=$trc
364		fi
365	fi
366
367	echo $rc
368	)
369}
370
371#
372# /etc/mnttab has the most recent mounts last.  Reverse it so that we
373# may umount in opposite order to the original mounts.
374#
375
376if [ ! -x /usr/bin/tail ]; then
377	exec < $MNTTAB
378	REVERSED=
379	while read line; do
380		if [ -n "$REVERSED" ]; then
381        		REVERSED="$line\n$REVERSED"
382		else
383			REVERSED="$line"
384		fi
385	done
386
387	error=`echo $REVERSED | doumounts`
388else
389	error=`tail -r $MNTTAB | doumounts`
390fi
391
392exit $error
393