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 2008 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 # Check and if needed sync the boot archive before unmounting everything.
 173 #
 174 if [ -z "${RFLAG}${NFLAG}${HFLAG}${FSType}" -a "$ZONENAME" = "global" \
 175     -a -x /sbin/bootadm ] ; then
 176         /sbin/bootadm -a update_all
 177 fi
 178 
 179 
 180 #
 181 # If we are in deferred activation patching, and the caller is 
 182 # svc.startd, then exit without unmounting any of the remaining 
 183 # file systems since the call path is from shutdown.  Note that
 184 # by the time we get here, smf stop methods for nfs, cachefs
 185 # etc, will have run.  
 186 #
 187 if [ -f $DEFERRED_ACTIVATION_PATCH_FLAG ] ; then 
 188         ppid=`ps -o ppid= -p $$`        # parent of umountall will be sh
 189                                         # from system()
 190 
 191         ppid=`ps -o ppid= -p $ppid`     # parent of sh will be svc.startd
 192         COMM=`ps -o comm= -p $ppid`
 193         if [ "$COMM" = "$SVC_STARTD" ] ; then
 194                 exit
 195         fi
 196 fi
 197 
 198 #
 199 # Take advantage of parallel unmounting at this point if we have no
 200 # criteria to match and we are in the global zone
 201 #
 202 if [ -z "${SFLAG}${LFLAG}${RFLAG}${HFLAG}${KFLAG}${FFLAG}${ZFLAG}" -a \
 203     "$ZONENAME" = "global" ]; then
 204         umount -a ${UMOUNTFLAG}
 205         exit                    # with return code of the umount -a
 206 fi
 207 
 208 #
 209 # Catch uses of /usr commands when /usr is not mounted
 210 if [ -n "$KFLAG" -a -z "$NFLAG" ]; then
 211         if [ ! -x /usr/sbin/fuser ]; then
 212                 fuser () {
 213                         echo "umountall: fuser -k skipped (no /usr)" 1>&2
 214                         # continue - not fatal
 215                 }
 216                 sleep () {
 217                         : # no point in sleeping if fuser is doing nothing
 218                 }
 219         else
 220                 if [ ! -x /usr/bin/sleep ]; then
 221                         sleep () {
 222                                 echo "umountall: sleep after fuser -k skipped (no /usr)" 1>&2
 223                                 # continue - not fatal
 224                         }
 225                 fi
 226         fi
 227 fi
 228 
 229 #
 230 # Shell function to avoid using /usr/bin/cut.  Given a dev from a
 231 # fstype=nfs line in mnttab (eg, "host:/export) extract the host
 232 # component.
 233 print_host () {
 234         OIFS=$IFS
 235         IFS=":"
 236         set -- $*
 237         echo $1
 238         IFS=$OIFS
 239 }
 240 
 241 #
 242 # doumounts echos its return code to stdout, so commands used within
 243 # this function should take care to produce no other output to stdout.
 244 doumounts () {
 245         (
 246         rc=0
 247         fslist=""
 248         nfslist=""
 249         while read dev mountp fstype mode dummy
 250         do
 251                 case "${mountp}" in
 252                 /                       | \
 253                 /dev                    | \
 254                 /dev/fd                 | \
 255                 /devices                | \
 256                 /etc/mnttab             | \
 257                 /etc/svc/volatile       | \
 258                 /lib                    | \
 259                 /proc                   | \
 260                 /sbin                   | \
 261                 /system/contract        | \
 262                 /system/object          | \
 263                 /tmp                    | \
 264                 /usr                    | \
 265                 /var                    | \
 266                 /var/adm                | \
 267                 /var/run                | \
 268                 '' )
 269                         #
 270                         # file systems possibly mounted in the kernel or
 271                         # in the methods of some of the file system
 272                         # services
 273                         #
 274                         continue
 275                         ;;
 276                 * )
 277                         if [ -n "$HFLAG" ]; then
 278                                 if [ "$fstype" = "nfs" ]; then
 279                                         thishost=`print_host $dev`
 280                                         if [ "$HOST" != "$thishost" ]; then
 281                                                 continue
 282                                         fi
 283                                 else
 284                                         continue
 285                                 fi
 286                         fi
 287                         if [ -n "$FFLAG" -a "$FSType" != "$fstype" ]; then
 288                                 continue
 289                         fi
 290                         if [ -n "$LFLAG" -a "$fstype" = "nfs" ]; then
 291                                 nfslist="$nfslist $mountp"
 292                                 continue
 293                         fi
 294                         #
 295                         # This will filter out autofs mounts with nfs file
 296                         # system mounted on the top of it.
 297                         #
 298                         # WARNING: use of any syscall on a NFS file system has
 299                         # the danger to go over-the-wire and could cause nfs
 300                         # clients to hang on shutdown, if the nfs server is
 301                         # down beforehand.
 302                         # For the reason described above, a simple test like 
 303                         # "df -F nfs $mountp" can't be used to filter out
 304                         # nfs-over-autofs mounts. We loop over a list instead:
 305                         #
 306                         if [ -n "$LFLAG" -a -n "$nfslist" -a "$fstype" = "autofs" ]
 307                         then
 308                                 for m in $nfslist; do
 309                                         if [ "$mountp" = "$m" ]; then
 310                                                 # Resume the outer while loop
 311                                                 continue 2
 312                                         fi
 313                                 done
 314                         fi
 315                         if [ -n "$RFLAG" -a "$fstype" != "nfs" ]; then
 316                                 continue
 317                         fi
 318                         if [ "$ZONENAME" != "global" ]; then
 319                                 for option in `echo $mode | tr , '\012'`; do
 320                                         #
 321                                         # should not see any zone options
 322                                         # but our own
 323                                         #
 324                                         if [ "$option" = "zone=$ZONENAME" ]; then
 325                                                 break
 326                                         fi
 327                                 done
 328                                 if [ "$option" != "zone=$ZONENAME" ]; then
 329                                         continue
 330                                 fi
 331                         # we are called from the global zone
 332                         else 
 333                                 for option in `echo $mode | tr , '\012'`; do
 334                                         case "$option" in
 335                                         zone=*)
 336                                                 option="zone="
 337                                                 break
 338                                         ;;
 339                                         esac
 340                                 done
 341                                 # skip mounts from non-global zones if ZFLAG is not set
 342                                 if [ "$option" = "zone=" -a -z "$ZFLAG" ]; then
 343                                         continue
 344                                 fi
 345                                 # skip mounts from the global zone if ZFLAG is set
 346                                 if [ "$option" != "zone=" -a -n "$ZFLAG" ]; then
 347                                         continue
 348                                 fi
 349                         fi
 350                         if [ -n "${KFLAG}" ]; then
 351                                 fuser -c -k $mountp 1>&2
 352                                 sleep 2
 353                         fi
 354                         if [ -n "$SFLAG" ]; then
 355                                 umount ${UMOUNTFLAG} ${mountp} 1>&2
 356                                 trc=$?
 357                                 if [ $trc -ne 0 ]; then
 358                                         rc=$trc
 359                                 fi
 360                         else
 361                                 # We want to umount in parallel
 362                                 fslist="$fslist $mountp"
 363                         fi
 364                 esac
 365         done
 366 
 367         if [ -n "$fslist" ]; then
 368                 umount -a ${UMOUNTFLAG} $fslist 1>&2
 369                 trc=$?
 370                 if [ $trc -ne 0 ]; then
 371                         rc=$trc
 372                 fi
 373         fi
 374 
 375         echo $rc
 376         )
 377 }
 378 
 379 #
 380 # /etc/mnttab has the most recent mounts last.  Reverse it so that we
 381 # may umount in opposite order to the original mounts.
 382 #
 383 
 384 if [ ! -x /usr/bin/tail ]; then
 385         exec < $MNTTAB
 386         REVERSED=
 387         while read line; do
 388                 if [ -n "$REVERSED" ]; then
 389                         REVERSED="$line\n$REVERSED"
 390                 else
 391                         REVERSED="$line"
 392                 fi
 393         done
 394 
 395         error=`echo $REVERSED | doumounts`
 396 else
 397         error=`tail -r $MNTTAB | doumounts`
 398 fi
 399 
 400 exit $error