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
31 usage () {
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
40 MNTTAB=/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
72 PATH=/sbin:/usr/sbin:/usr/bin
73
74 DEFERRED_ACTIVATION_PATCH_FLAG="/var/run/.patch_loopback_mode"
75 SVC_STARTD="/lib/svc/bin/svc.startd"
76
77 # Clear these in case they were already set in our inherited environment.
78 FSType=
79 FFLAG=
80 HOST=
81 HFLAG=
82 RFLAG=
83 LFLAG=
84 SFLAG=
85 KFLAG=
86 ZFLAG=
87 NFLAG=
88 LOCALNAME=
89 UMOUNTFLAG=
90
91
92 while getopts ?rslkF:h:Zn c
93 do
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
131 done
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
142 if [ $# -ge $OPTIND ]; then # 1
143 usage "additional arguments not supported"
144 fi
145
146 if [ -n "$RFLAG" -a -n "$LFLAG" ]; then # 2
147 usage "options -r and -l are incompatible"
148 fi
149
150 if [ \( -n "$RFLAG" -o -n "$LFLAG" \) -a "$HFLAG" = "h" ]; then # 3
151 usage "option -${RFLAG}${LFLAG} incompatible with -h option"
152 fi
153
154 if [ -n "$FFLAG" -a "$HFLAG" = "h" ]; then # 4
155 usage "Specifying FStype incompatible with -h option"
156 fi
157
158 if [ -n "$HFLAG" -a "$HOST" = "$LOCALNAME" ]; then # 5
159 usage "Specifying local host illegal for -h option"
160 fi
161
162 if [ "$FSType" = "nfs" -a "$LFLAG" = "l" ]; then # 6
163 usage "option -l and FSType nfs are incompatible"
164 fi
165
166 if [ -n "$FFLAG" -a "$FSType" != "nfs" -a -n "$RFLAG" ]; then # 7
167 usage "option -r and FSType ${FSType} are incompatible"
168 fi
169
170 ZONENAME=`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 #
179 if [ -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
188 fi
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 #
194 if [ -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
198 fi
199
200 #
201 # Catch uses of /usr commands when /usr is not mounted
202 if [ -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
219 fi
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.
225 print_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.
236 doumounts () {
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
376 if [ ! -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`
388 else
389 error=`tail -r $MNTTAB | doumounts`
390 fi
391
392 exit $error