#!/sbin/sh # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # # Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # # proc usage { if test -n $1 { echo "umountall: $1" 1>&2 } echo "Usage:\n\tumountall [-k] [-s] [-F FSType] [-l|-r] [-Z] [-n]" 1>&2 echo "\tumountall [-k] [-s] [-h host] [-Z] [-n]" 1>&2 exit 2 } setvar MNTTAB = '/etc/mnttab' # This script is installed as both /sbin/umountall (as used in some # /sbin/rc? and /etc/init.d scripts) _and_ as /usr/sbin/umountall (typically # PATHed from the command line). As such it should not depend on /usr # being mounted (if /usr is a separate filesystem). # # /sbin/sh Bourne shell builtins we use: # echo # exit # getopts # test, [ ] # exec # read # # /sbin commands we use: # /sbin/uname # /sbin/umount # # The following /usr based commands may be used by this script (depending on # command line options). We will set our PATH to find them, but where they # are not present (eg, if /usr is not mounted) we will catch references to # them via shell functions conditionally defined after option processing # (don't use any of these commands before then). # # Command Command line option and use # /usr/bin/sleep -k, to sleep after an fuser -c -k on the mountpoint # /usr/sbin/fuser -k, to kill processes keeping a mount point busy # # In addition, we use /usr/bin/tail if it is available; if not we use # slower shell constructs to reverse a file. setvar PATH = "/sbin:/usr/sbin:/usr/bin" # Clear these in case they were already set in our inherited environment. setvar FSType = '' setvar FFLAG = '' setvar HOST = '' setvar HFLAG = '' setvar RFLAG = '' setvar LFLAG = '' setvar SFLAG = '' setvar KFLAG = '' setvar ZFLAG = '' setvar NFLAG = '' setvar LOCALNAME = '' setvar UMOUNTFLAG = '' setvar RemoteFSTypes = '' # Is the passed fstype a "remote" one? # Essentially: /usr/bin/grep "^$1" /etc/dfs/fstypes proc isremote { for t in [$RemoteFSTypes] { test $t = $1 && return 0 } return 1 } # Get list of remote FS types (just once) setvar RemoteFSTypes = $(while read t junk { echo $t; }) # # Process command line args # while getopts ?rslkF:h:Zn c { match $c { with r setvar RFLAG = ""r"" with l setvar LFLAG = ""l"" with s setvar SFLAG = ""s"" with k setvar KFLAG = ""k"" with h if test -n $HFLAG { usage "more than one host specified" } setvar HOST = "$OPTARG" setvar HFLAG = ""h"" setvar LOCALNAME = $(uname -n) with F if test -n $FFLAG { usage "more than one FStype specified" } setvar FSType = "$OPTARG" setvar FFLAG = ""f"" match $FSType { with ?????????* usage "FSType ${FSType} exceeds 8 characters" }; with Z setvar ZFLAG = ""z"" with n setvar NFLAG = ""n"" # Alias any commands that would perform real actions to # something that tells what action would have been performed setvar UMOUNTFLAG = ""-V"" proc fuser { echo "fuser $ifsjoin(ARGV)" 1>&2 } proc sleep { : # No need to show where we'd sleep } with \? usage "" } } # Sanity checking: # 1) arguments beyond those supported # 2) can't specify both remote and local # 3) can't specify a host with -r or -l # 4) can't specify a fstype with -h # 5) can't specify this host with -h (checks only uname -n) # 6) can't be fstype nfs and local # 7) only fstype nfs is remote if test $Argc -ge $OPTIND { # 1 usage "additional arguments not supported" } if test -n $RFLAG -a -n $LFLAG { # 2 usage "options -r and -l are incompatible" } if test '(' -n $RFLAG -o -n $LFLAG ')' -a $HFLAG = "h" { # 3 usage "option -${RFLAG}${LFLAG} incompatible with -h option" } if test -n $FFLAG -a $HFLAG = "h" { # 4 usage "Specifying FStype incompatible with -h option" } if test -n $HFLAG -a $HOST = $LOCALNAME { # 5 usage "Specifying local host illegal for -h option" } if test $LFLAG = "l" -a -n $FSType { # 6 # remote FSType not allowed isremote $FSType && usage "option -l and FSType ${FSType} are incompatible" } if test $RFLAG = "r" -a -n $FSType { # 7 # remote FSType required isremote $FSType || usage "option -r and FSType ${FSType} are incompatible" } setvar ZONENAME = $(zonename) # # Take advantage of parallel unmounting at this point if we have no # criteria to match and we are in the global zone # if test -z "${SFLAG}${LFLAG}${RFLAG}${HFLAG}${KFLAG}${FFLAG}${ZFLAG}" -a \ $ZONENAME = "global" { umount -a ${UMOUNTFLAG} exit # with return code of the umount -a } # # Catch uses of /usr commands when /usr is not mounted if test -n $KFLAG -a -z $NFLAG { if test ! -x /usr/sbin/fuser { proc fuser { echo "umountall: fuser -k skipped (no /usr)" 1>&2 # continue - not fatal } proc sleep { : # no point in sleeping if fuser is doing nothing } } else { if test ! -x /usr/bin/sleep { proc sleep { echo "umountall: sleep after fuser -k skipped (no /usr)" 1>&2 # continue - not fatal } } } } # # Shell function to avoid using /usr/bin/cut. Given a dev from a # fstype=nfs line in mnttab (eg, "host:/export) extract the host # component. The dev string looks like: "host:/path" proc print_nfs_host { setvar OIFS = "$IFS" setvar IFS = "":"" set -- $ifsjoin(ARGV) echo $1 setvar IFS = "$OIFS" } # # Similar for smbfs, but tricky due to the optional parts # of the "device" syntax. The dev strings look like: # "//server/share" or "//user@server/share" proc print_smbfs_host { setvar OIFS = "$IFS" setvar IFS = ""/@"" set -- $ifsjoin(ARGV) match $Argc { with 3 echo $2 with 2 echo $1 } setvar IFS = "$OIFS" } # # doumounts echos its return code to stdout, so commands used within # this function should take care to produce no other output to stdout. proc doumounts { shell { setvar rc = '0' setvar fslist = """" while read dev mountp fstype mode dummy { match ${mountp} { with / | \ /dev | \ /dev/fd | \ /devices | \ /etc/mnttab | \ /etc/svc/volatile | \ /lib | \ /proc | \ /sbin | \ /system/contract | \ /system/object | \ /tmp | \ /tmp/.libgrubmgmt* | \ /usr | \ /var | \ /var/adm | \ /var/run | \ '' # # file systems possibly mounted in the kernel or # in the methods of some of the file system # services # continue with * if test -n $HFLAG { setvar thishost = ''-'' if test $fstype = "nfs" { setvar thishost = $(print_nfs_host $dev) } if test $fstype = "smbfs" { setvar thishost = $(print_smbfs_host $dev) } if test $HOST != $thishost { continue } } if test -n $FFLAG -a $FSType != $fstype { continue } if test -n $LFLAG { # umount local filesystems isremote $fstype && continue } # Note: isremote is true for both nfs & autofs, so # this will filter out autofs mounts with nfs file # system mounted on the top of it. # # WARNING: use of any syscall on a NFS file system has # the danger to go over-the-wire and could cause nfs # clients to hang on shutdown, if the nfs server is # down beforehand. # For the reason described above, a simple test like # "df -F nfs $mountp" can't be used to filter out # nfs-over-autofs mounts. (isremote works OK) # if test -n $RFLAG { # umount remote filesystems isremote $fstype || continue } if test $ZONENAME != "global" { for option in [$(echo $mode | tr , '\012)] { # # should not see any zone options # but our own # if test $option = "zone=$ZONENAME" { break } } if test $option != "zone=$ZONENAME" { continue } # we are called from the global zone } else { for option in [$(echo $mode | tr , '\012)] { match $option { with zone=* setvar option = ""zone="" break } } # skip mounts from non-global zones if ZFLAG is not set if test $option = "zone=" -a -z $ZFLAG { continue } # skip mounts from the global zone if ZFLAG is set if test $option != "zone=" -a -n $ZFLAG { continue } } if test -n ${KFLAG} { fuser -c -k $mountp 1>&2 sleep 2 } if test -n $SFLAG { umount ${UMOUNTFLAG} ${mountp} 1>&2 setvar trc = "$Status" if test $trc -ne 0 { setvar rc = "$trc" } } else { # We want to umount in parallel setvar fslist = ""$fslist $mountp"" } } } if test -n $fslist { umount -a ${UMOUNTFLAG} $fslist 1>&2 setvar trc = "$Status" if test $trc -ne 0 { setvar rc = "$trc" } } echo $rc } } # # /etc/mnttab has the most recent mounts last. Reverse it so that we # may umount in opposite order to the original mounts. # if test ! -x /usr/bin/tail { exec < $MNTTAB setvar REVERSED = '' while read line { if test -n $REVERSED { setvar REVERSED = ""$line\n$REVERSED"" } else { setvar REVERSED = "$line" } } setvar error = $(echo $REVERSED | doumounts) } else { setvar error = $(tail -r $MNTTAB | doumounts) } exit $error