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