xref: /illumos-gate/usr/src/cmd/initpkg/umountall.sh (revision 812e8c05)
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=
86RemoteFSTypes=
87
88# Is the passed fstype a "remote" one?
89# Essentially: /usr/bin/grep "^$1" /etc/dfs/fstypes
90isremote() {
91	for t in $RemoteFSTypes
92	do
93		[ "$t" = "$1" ] && return 0
94	done
95	return 1
96}
97
98# Get list of remote FS types (just once)
99RemoteFSTypes=`while read t junk; do echo $t; done < /etc/dfs/fstypes`
100
101
102#
103# Process command line args
104#
105while getopts ?rslkF:h:Zn c
106do
107	case $c in
108	r)	RFLAG="r";;
109	l)	LFLAG="l";;
110	s)	SFLAG="s";;
111	k)	KFLAG="k";;
112	h)	if [ -n "$HFLAG" ]; then
113			usage "more than one host specified"
114		fi
115		HOST=$OPTARG
116		HFLAG="h"
117		LOCALNAME=`uname -n`
118		;;
119	F)	if [ -n "$FFLAG" ]; then
120			usage "more than one FStype specified"
121		fi
122		FSType=$OPTARG
123		FFLAG="f"
124		case $FSType in
125		?????????*)
126			usage "FSType ${FSType} exceeds 8 characters"
127		esac;
128		;;
129	Z)	ZFLAG="z";;
130	n)	NFLAG="n"
131		# Alias any commands that would perform real actions to
132		# something that tells what action would have been performed
133		UMOUNTFLAG="-V"
134		fuser () {
135			echo "fuser $*" 1>&2
136		}
137		sleep () {
138			: # No need to show where we'd sleep
139		}
140		;;
141	\?)	usage ""
142		;;
143	esac
144done
145
146# Sanity checking:
147#	1) arguments beyond those supported
148#	2) can't specify both remote and local
149#	3) can't specify a host with -r or -l
150#	4) can't specify a fstype with -h
151#	5) can't specify this host with -h (checks only uname -n)
152#	6) can't be fstype nfs and local
153#	7) only fstype nfs is remote
154
155if [ $# -ge $OPTIND ]; then						# 1
156	usage "additional arguments not supported"
157fi
158
159if [ -n "$RFLAG" -a -n "$LFLAG" ]; then					# 2
160	usage "options -r and -l are incompatible"
161fi
162
163if [ \( -n "$RFLAG" -o -n "$LFLAG" \) -a "$HFLAG" = "h" ]; then		# 3
164	usage "option -${RFLAG}${LFLAG} incompatible with -h option"
165fi
166
167if [ -n "$FFLAG" -a "$HFLAG" = "h" ]; then				# 4
168	usage "Specifying FStype incompatible with -h option"
169fi
170
171if [ -n "$HFLAG" -a "$HOST" = "$LOCALNAME" ]; then			# 5
172	usage "Specifying local host illegal for -h option"
173fi
174
175if [ "$LFLAG" = "l" -a -n "$FSType" ]; then				# 6
176	# remote FSType not allowed
177	isremote "$FSType" &&
178	usage "option -l and FSType ${FSType} are incompatible"
179fi
180
181if [ "$RFLAG" = "r" -a -n "$FSType" ]; then				# 7
182	# remote FSType required
183	isremote "$FSType" ||
184	usage "option -r and FSType ${FSType} are incompatible"
185fi
186
187ZONENAME=`zonename`
188
189#
190# Take advantage of parallel unmounting at this point if we have no
191# criteria to match and we are in the global zone
192#
193if [ -z "${SFLAG}${LFLAG}${RFLAG}${HFLAG}${KFLAG}${FFLAG}${ZFLAG}" -a \
194    "$ZONENAME" = "global" ]; then
195	umount -a ${UMOUNTFLAG}
196	exit			# with return code of the umount -a
197fi
198
199#
200# Catch uses of /usr commands when /usr is not mounted
201if [ -n "$KFLAG" -a -z "$NFLAG" ]; then
202	if [ ! -x /usr/sbin/fuser ]; then
203		fuser () {
204			echo "umountall: fuser -k skipped (no /usr)" 1>&2
205			# continue - not fatal
206		}
207		sleep () {
208			: # no point in sleeping if fuser is doing nothing
209		}
210	else
211		if [ ! -x /usr/bin/sleep ]; then
212			sleep () {
213		echo "umountall: sleep after fuser -k skipped (no /usr)" 1>&2
214				# continue - not fatal
215			}
216		fi
217	fi
218fi
219
220#
221# Shell function to avoid using /usr/bin/cut.  Given a dev from a
222# fstype=nfs line in mnttab (eg, "host:/export) extract the host
223# component.  The dev string looks like: "host:/path"
224print_nfs_host () {
225	OIFS=$IFS
226	IFS=":"
227	set -- $*
228	echo $1
229	IFS=$OIFS
230}
231#
232# Similar for smbfs, but tricky due to the optional parts
233# of the "device" syntax.  The dev strings look like:
234# "//server/share" or "//user@server/share"
235print_smbfs_host () {
236	OIFS=$IFS
237	IFS="/@"
238	set -- $*
239	case $# in
240	3) echo "$2";;
241	2) echo "$1";;
242	esac
243	IFS=$OIFS
244}
245
246#
247# doumounts echos its return code to stdout, so commands used within
248# this function should take care to produce no other output to stdout.
249doumounts () {
250	(
251	rc=0
252	fslist=""
253	while read dev mountp fstype mode dummy
254	do
255		case "${mountp}" in
256		/			| \
257		/dev			| \
258		/dev/fd			| \
259		/devices		| \
260		/etc/mnttab		| \
261		/etc/svc/volatile	| \
262		/lib			| \
263		/proc			| \
264		/sbin			| \
265		/system/contract	| \
266		/system/object		| \
267		/tmp			| \
268		/tmp/.libgrubmgmt*	| \
269		/usr			| \
270		/var			| \
271		/var/adm		| \
272		/var/run		| \
273		'' )
274			#
275			# file systems possibly mounted in the kernel or
276			# in the methods of some of the file system
277			# services
278			#
279			continue
280			;;
281		* )
282			if [ -n "$HFLAG" ]; then
283				thishost='-'
284				if [ "$fstype" = "nfs" ]; then
285					thishost=`print_nfs_host $dev`
286				fi
287				if [ "$fstype" = "smbfs" ]; then
288					thishost=`print_smbfs_host $dev`
289				fi
290				if [ "$HOST" != "$thishost" ]; then
291					continue
292				fi
293			fi
294			if [ -n "$FFLAG" -a "$FSType" != "$fstype" ]; then
295				continue
296			fi
297
298			if [ -n "$LFLAG" ]; then
299				# umount local filesystems
300				isremote "$fstype" && continue
301			fi
302
303			# Note: isremote is true for both nfs & autofs, so
304			# this will filter out autofs mounts with nfs file
305			# system mounted on the top of it.
306			#
307			# WARNING: use of any syscall on a NFS file system has
308			# the danger to go over-the-wire and could cause nfs
309			# clients to hang on shutdown, if the nfs server is
310			# down beforehand.
311			# For the reason described above, a simple test like
312			# "df -F nfs $mountp" can't be used to filter out
313			# nfs-over-autofs mounts.  (isremote works OK)
314			#
315			if [ -n "$RFLAG" ]; then
316				# umount remote filesystems
317				isremote "$fstype" || continue
318			fi
319			if [ "$ZONENAME" != "global" ]; then
320				for option in `echo $mode | tr , '\012'`; do
321					#
322					# should not see any zone options
323					# but our own
324					#
325					if [ "$option" = "zone=$ZONENAME" ]; then
326						break
327					fi
328				done
329				if [ "$option" != "zone=$ZONENAME" ]; then
330					continue
331				fi
332			# we are called from the global zone
333			else
334				for option in `echo $mode | tr , '\012'`; do
335					case "$option" in
336					zone=*)
337						option="zone="
338						break
339					;;
340					esac
341				done
342				# skip mounts from non-global zones if ZFLAG is not set
343				if [ "$option" = "zone=" -a -z "$ZFLAG" ]; then
344					continue
345				fi
346				# skip mounts from the global zone if ZFLAG is set
347				if [ "$option" != "zone=" -a -n "$ZFLAG" ]; then
348					continue
349				fi
350			fi
351			if [ -n "${KFLAG}" ]; then
352				fuser -c -k $mountp 1>&2
353				sleep 2
354			fi
355			if [ -n "$SFLAG" ]; then
356				umount ${UMOUNTFLAG} ${mountp} 1>&2
357				trc=$?
358				if [ $trc -ne 0 ]; then
359					rc=$trc
360				fi
361			else
362				# We want to umount in parallel
363				fslist="$fslist $mountp"
364			fi
365		esac
366	done
367
368	if [ -n "$fslist" ]; then
369		umount -a ${UMOUNTFLAG} $fslist 1>&2
370		trc=$?
371		if [ $trc -ne 0 ]; then
372			rc=$trc
373		fi
374	fi
375
376	echo $rc
377	)
378}
379
380#
381# /etc/mnttab has the most recent mounts last.  Reverse it so that we
382# may umount in opposite order to the original mounts.
383#
384
385if [ ! -x /usr/bin/tail ]; then
386	exec < $MNTTAB
387	REVERSED=
388	while read line; do
389		if [ -n "$REVERSED" ]; then
390        		REVERSED="$line\n$REVERSED"
391		else
392			REVERSED="$line"
393		fi
394	done
395
396	error=`echo $REVERSED | doumounts`
397else
398	error=`tail -r $MNTTAB | doumounts`
399fi
400
401exit $error
402