#!/bin/bash # TODO: accept either dir or device on command line (like umount does) # ie: /mnt/tmp or /dev/hda6 # TODO: accept relative dirs # ie: cd /mnt; fumount tmp declare -rx SCRIPT=${0##*/} declare -rx VERSION=001 if [ `whoami` != "root" ]; then echo "You must run this as root" exit 1 fi # display script usage, then exit function usage { printf "usage: $SCRIPT [-i] [-v] dir\n" >&2 printf " -i : interactive mode\n" >&2 printf " -v : verbose\n" >&2 success=1 # don't print failure message exit 1 } # print verbose/debug information function debug { if [ -n "$verbose" ]; then printf "$1" fi } # umount was a success function success { success=1 debug "$mntpoint unmounted\n" exit 0 } function on_exit { if [ -z "$success" ]; then printf "unmount failed\n" >&2 fi } trap on_exit EXIT # parse the arguments while [ -n "$1" ]; do case $1 in -i) interactive=1 ;; -v) verbose=1 ;; -*) usage ;; *) if [ -z "$mntpoint" ]; then mntpoint=$1 else usage fi ;; esac shift done if [ -z "$mntpoint" ]; then usage fi # strip trailing slash from mntpoint # surely there must be a better way to do this... while [ "`echo "$mntpoint" |grep '/$'`" ]; do mntpoint=${mntpoint%/} done # attempt a regular umount tryumount=`umount $mntpoint 2>&1` ret=$? if [ "`echo $tryumount |grep 'not mounted'`" ]; then printf "%s is not mounted\n" "$mntpoint" >&2 if [ -e "$mntpoint" ]; then printf "You must provide the full path (ie: /mnt/tmp)\n" >&2 fi exit 1 fi if [ "`echo $tryumount |grep 'not found'`" ]; then printf "%s not fount\n" "$mntpoint" >&2 exit 1 fi # did the umount succeed? if [ $ret -eq 0 ]; then success else # if the filesystem is busy, kill all processes accessing it if [ "`echo $tryumount |grep 'busy'`" ]; then debug "busy... killing all processes accessing the filesystem\n" if [ "$interactive" ]; then fuser -ki $mntpoint else fuser -k $mntpoint &> /dev/null fi else printf "%s failed... umount returned %d\n" "$SCRIPT" "$ret" >&2 exit 1 fi fi # try to umount again tryumount=`umount $mntpoint 2>&1` ret=$? if [ $ret -eq 0 ]; then success else if [ "`echo $tryumount | grep 'busy'`" ]; then # get the name of the device that is mounted exec 6<&0 # save stdin... better way to do this? exec < /proc/mounts while read line; do mnt=`echo $line |awk '{print $2}'` if [ "$mnt" = "$mntpoint" ]; then device=`echo $line |awk '{print $1}'` break fi done exec 0<&6 6<&- # restore stdin # our first attempt didn't work, do lazy umount to make it easier debug "still busy, doing lazy umount\n" if [ "$interactive" ]; then printf "Do a lazy unmount? (y/N)\n" read yn if [ "$yn" != "y" ]; then exit 1 fi fi umount -l $mntpoint # keep trying to kill processes accessing the device. don't exit until # we succeed while [ -n "$device" -a -e "$device" ]; do debug "attempting to kill all processes accessing $device\n" if [ "$interactive" ]; then fuser -kmi $device else fuser -km $device &> /dev/null fi if [ -z "`fuser -m $device`" ]; then success fi done printf "Lazy unmount succeeded, but I was unable to find the\n" printf "device the filesystem was mounted on. There may still be\n" printf "processes accessing the device.\n" exit 0 else printf "%s failed... umount returned %d\n" "$SCRIPT" "$ret" >&2 exit 1 fi fi