#!/bin/bash
#
# Usage:
#    export DEVICE=<device>; usb-mount
#    usb-mount <mountpoint>             # not implemented
#    usb-mount                          # mounts anything thats Attached.
#    usb-umount <mountpoint>
#    
# The first usage is intended for /etc/hotplug/usb-usb-storage,
# but due to some issues with identifying the device - the second
# is always used.
# 
# The second and third usages are for use by user programs - with 
# the assumption that there is a sudoers entry for the script.
#
# Michael Hamilton
# michael@actrix.gen.nz
# October 2002
# GNU Public Licence applies.
#
# Version: v0.94 (October 2004)
#  - Use diskpart to detect whether there really is a valid partition table.
#  - Diskpart is now required.
#  - Fix NO_PARTITIONS_SUFFIX code and set to ""
#  - Change to old style awk -v argument to support distros that use mawk.
#  - Devices with more than one partition are mounted as device-M/partition-M
#  - Devices with more than one partition are umounted as one.
#  - Mount option of shortname=mixed for fat32
#  - Add a SUDO variable in case sudo isn't in the default path.
#  - Added USE_WHO option for non redhat distros.
#  - Fixed 2.4 umount partition scanning code - was missing some mount points.
#  - Fixed lockfile code.
# Version: v0.93 (July 2004)
#  - Fixed ownership of desktop files on Icon changes.
#  - Added a directory mask.
#  - Made the location of lockfile a config option.
#  - Finished desktop signal actions that accidentally made it into v0.92.
#  - Made the terminate signal default to -HUP
#  - Use -o vfat for FAT16 - fixes mouting problems for some users.
# Version: v0.92 (July 2004)
#  - Cleaned up kernel 2.4 versus 2.6 division of code.
#  - Under kernel 2.6 Use full SCSI id of <scsi_host_number> <bus> <scsi_id> <lun> 
#  - Use fuser to identify and optionally terminate processes.
#  - Added TERMINATE_SIGNAL and KILL_SIGNAL options.
#  - Refuse to umount devices that are in use unless -l is in UMOUNT_OPTIONS.
#  - Remove unnecessary -a sg_map option and ALPHABETIC_DEVICE_NAMES option.
# Version: v0.91 (June 2004)
#  - Extra safety check on umount by Jeff Minelli
#  - Add dirsync from Bart Samwel
#  - Add an ALPHABETIC_DEVICE_NAMES option for Mandrake etc.
#  - Cleanup mount options
#  - Try to resolve the no-partitions case via an NO_PARTITIONS_SUFFIX option.
# Version: v0.9 (March 2004)
#  - Use sg_map and mount to detect detached mount points under kernel 2.6.
#  - Use procfs to detect detached mount points under kernel 2.4.
#    This eliminates the need to save this info in /var/usb.
#    From a suggestion by Alan McCluskey.
#  - If not already there - make /var/usb for the lockfile - fix
#    from Richard Tresidder.
#  - Fix the use of MOUNT_OPTIONS in the absense of disktype.
#  - Cleaned up some messages.
# Version: v0.8 (March 2004)
#  - Improved behavior on kernel 2.6 - get rid of bogus messages.
#  - Added MOUNT_OPTIONS setting to control -o sync
#  - Added UMOUNT_OPTIONS setting to control -lf
#  - Improved feedback messages. 
#  - Added (slightly) more accurate version history comments.
#  - Removed config parameters to an /etc or /usr/local/etc file
#    to make upgrades easier.  A config file is now required - don't
#    allow anyone to blindly use the script without thinking about
#    it's setup.
#  - Added a DEBUG setting. 
#  Improvements by Richard Tresidder (rtresidd@tresar-electronics.com.au)
#  - fixed problem with sed replacement syntax for sed version 3.02.
#  - fixed problem with umount (removed -l and -f) option 
#      -l: Lazy unmount, defers the umount if the device is busy. 
#      -f: force.
#    RT: "I had found on numerous occasions that this never actually 
#    unmounted the usb key" - on Redhat 7.2.
#  - added synchronous write feature to mount options. May be slower 
#    but makes sure it gets there... hence much better for hot-plug devices.
#  - feedback message improvements.
#
# Version: v0.7
#  - Finally have a way to correctly id the scsi device - uses 
#    sg_map to map from scsi host number to /dev/sd[abc...]
#  - uses 2.6 features when available - can now identify exact device
#    added - used to just mount any that were unmounted.
#  - removed last code associated with GUID's - too unreliable - not 
#    all usb devices have them.
#
# Version: v0.5
#  - Fix bugs when plugging more than one device.
#  - Change installation instructions to fix bugs when plugging
#    more than one device.
#  Improvements by Yemu (yemu@gazeta.pl)
#  - added DELETE_ICON option for when a device is unplugged
#
# Version: v0.4
#  Improvements by Alessandro Fiorino (alessandro@fiorino.name)
#  - Recognise partitions on each usb-drive and mount them individually.
#  - Use -f and -l on umount
#  - Add optional creation of .directory files in mounted folders.
#  - requires the disktype utility for partition recongnition
# 
# Version: v0.3
#  - Fix option for hardwiring the USB_USER so that icons are 
#    correctly located.
#
# Version: v0.2
#  - Steffen Muehlnickel's null guid suggestion
#

# These settings correspond to those embedded in version v0.7 
# except in respect to new MOUNT_OPTIONS where sync is enabled.
# You can override them in /etc/usb-mount.conf or 
# /usr/local/etc/usb-mount.conf - see sample config file
# for explanations.  These defaults are here to protect us 
# from any errors due a setting being missing from the config file.
MOUNT_PATH="/mnt/usb-storage"
DISKTYPE=/usr/bin/disktype
SGMAP=/usr/bin/sg_map
FDISK=/sbin/fdisk
SUDO=/usr/bin/sudo
LOCKFILE=/usr/bin/lockfile
VAR_DIR=/var/run/usb
NO_PARTITIONS_SUFFIX="1"
DO_DESKTOP=1
MOUNT_ICON_NAME="usbpendrive_mount"
UMOUNT_ICON_NAME="usbpendrive_umount"
# DELETE_ICON=1
DO_DIRECTORY_ICON=1
DO_CONSOLE_USER=1

#TERMINATE_SIGNAL="-HUP"
#KILL_SIGNAL="-KILL"
#ONLY_SIGNAL_ON_REQUEST=1
#DESKTOP_SIGNALS=1

USB_USER=root
USB_GROUP=users
USB_UMASK=0002
USB_DMASK=0002
# DO_MODULES=1

# consider adding the noatime option to save writes to CMOS 
MOUNT_OPTIONS="-o sync,dirsync,noatime"
UMOUNT_OPTIONS="-lf"

LOG_POPUP_LEVEL=emerg
FACILITY=local5
DEBUG=1

id=$(basename $0)

# Insist on at least one config file:
if [ ! -r /etc/usb-mount.conf -a ! -r /usr/local/etc/usb-mount.conf ]
then
    logger -t $id -p $FACILITY.$LOG_POPUP_LEVEL \
      "No configuration file, usb-mount.conf, in /etc or /usr/local/etc"  
    exit 1
fi

# Note that both config files will be read and 
# the local one can countermand the /etc one.
if [ -r /etc/usb-mount.conf ]
then
	. /etc/usb-mount.conf
fi

if [ -r /usr/local/etc/usb-mount.conf ]
then
	. /usr/local/etc/usb-mount.conf
fi

# An internal counter used to create a mount point directory
# name.
MOUNT_NUMBER=0

function errorExit {
    logger -t $id -p $FACILITY.$LOG_POPUP_LEVEL -- "$@"
    rm -f "$VAR_DIR/usb.lock"
    exit 1
}

function errorMessage {
    logger -t $id -p $FACILITY.$LOG_POPUP_LEVEL -- "$@"
}

function feedback {
    logger -t $id -p $FACILITY.info -- "$@"
}

function debugMessage {
    [ -n "$DEBUG" ] && logger -t $id -p $FACILITY.info -- "$@"
    #logger -t $id -p $FACILITY.debug -- "$@"
}

function scsiUniqIDFromDevPath {
   local devPath="$1"
   local uniqId="UNKNOWN"
   if ls /sys/$devPath/host* > /dev/null 2>&1
   then
        uniqId="`find /sys/$devPath/host* -name '[0-9]*:[0-9]*:[0-9]*:*[0-9]' -printf '%f\n'`" 
   	[ -z "$uniqId" ] && uniqId="UNKNOWN"
   fi
   feedback "DEVPATH yields SCSI unique id $uniqId from $1"
   echo $uniqId
}

function allUsbScsiUniqID {
    local usb=""

    if uname -r | grep -q '^2.4'
    then
	for usb in `find /proc/scsi/ -path '/proc/scsi/usb-storage*' -type f | xargs grep -l 'Attached: Yes'`
	do
	    local n="`scsiHostNumberFromUsbProc24 $usb`"
	    feedback "USB proc-fs yields SCSI host number=$n - suffix with zeros (kernel 2.4)"  
	    echo "$n:0:0:0"	    
	done
    else
	for usb in `find /sys/bus/usb/drivers/usb-storage -follow -iname 'host*'`
	do
	    find $usb -name '[0-9]*:[0-9]*:[0-9]*:*[0-9]' -printf '%f\n'
	done
    fi
}

function diskDevFromUniqID {
    local id="$1"
    local alpha=""
    local scsiDiskDev=`$SGMAP -sd -x | awk -v id="$id" '$2 ":" $3 ":" $4 ":" $5 == id && NF == 7 { print $7 }'`
    if [ -z "$scsiDiskDev" ]; then
        scsiDiskDev="UNKNOWN"
    fi
    feedback "SCSI disk device for $1 is $scsiDiskDev"
    echo $scsiDiskDev
}

function diskDevFromDevPath {
    local scsiUniqID=`scsiUniqIDFromDevPath $1`
    if [ "$scsiUniqID" == "UNKNOWN" ]
    then
        feedback "Disk device is UNKNOWN"
	echo "UNKNOWN"
	return 1
    fi
    local scsiDiskDev=$(diskDevFromUniqID $scsiUniqID)
    debugMessage "Disk device is $scsiDiskDev"
    echo "$scsiDiskDev"
}

function scsiPartitions {
    local scsiDiskDev="$1"
    feedback "Partition lookup for disk device=$scsiDiskDev"
    # make damm sure we have something to pass to fdisk
    [ -z "$scsiDiskDev" ] && errorExit "bad scsiDiskDev $scsiDiskDev"
    # Disktype reports authoritively on whether the device has partitions.
    if $DISKTYPE $scsiDiskDev | grep -q Partition
    then
        local scsiPartList=`$FDISK -l $scsiDiskDev | awk '/^\/dev/ {print $1}'`
        debugMessage "partitions=$scsiPartList."
        local fName=`echo $scsiDiskDev | sed 's#/#_#g'`
	feedback "Partition list for $scsiDiskDev is $scsiPartList"
	echo "${scsiPartList}"
    else
	feedback "No partition table for $scsiDiskDev - try ${scsiDiskDev}$NO_PARTITIONS_SUFFIX"
	echo "${scsiDiskDev}$NO_PARTITIONS_SUFFIX"
    fi
}

function checkProcesses {
    local mountPoint="$1"
    local do_signal="$2"
    local do_kill="$3"
    if [ -x /sbin/fuser ] &&  /sbin/fuser -m "$mountPoint" 1>/dev/null 2>&1
    then
	if [ -n "$do_signal" -a -n "$TERMINATE_SIGNAL" ] 
	then
	    feedback "Have to signal processes with $TERMINATE_SIGNAL"
	    /sbin/fuser -m -v -k $TERMINATE_SIGNAL "$mountPoint" 2>&1 | feedback
	    if [ -n "$do_kill" -a -n "$KILL_SIGNAL" ]
	    then
		sleep 2
		if /sbin/fuser -m "$mountPoint" 1>/dev/null 2>&1
		then
		    feedback "Have to kill processes with $KILL_SIGNAL"
		    /sbin/fuser -m -v -k $KILL_SIGNAL "$mountPoint" 2>&1 | feedback
		fi
	    fi
	fi
	if /sbin/fuser -v -m "$mountPoint" | egrep -q '[0-9]' 1>/dev/null 2>&1
	then
	    # Still being used - cannot umount unless using umount -l option
	    if echo "$UMOUNT_OPTIONS" | grep -q -- '-l'
 	    then
		feedback "Still used by processes: `/sbin/fuser -m -v $mountPoint | tail +3`"
	    else
		errorMessage "Cannot unmount - still used by processes: `/sbin/fuser -m -v $mountPoint | tail +3`"
		return 1
	    fi
	fi
    fi
    return 0
}

function umountDevice {

    local mountBasename="$1"
    local do_signal=1
    local do_kill=1

    if [ -n "$ONLY_SIGNAL_ON_REQUEST" ]
    then
	do_signal="$2"
	do_kill="$3"
    fi

    sync

    feedback "Umount request for $mountBasename" 

    if echo "$mountBasename" | egrep -q "^$MOUNT_PATH/[^/]+"
    then
	local mountPoint=""

	feedback "Check any mount points under $mountBasename" 
	# this will also umount any mounts within the mount point
	for mountPoint in `mount | awk -v mp=$mountBasename '$3 ~ "^" mp { print $3 }'`
	do
	    if checkProcesses "$mountPoint" "$do_signal" "$do_kill"
	    then
		{
		    if umount $UMOUNT_OPTIONS "$mountPoint" 
		    then 
			feedback "Completed unmount of $mountPoint"
		    else
			feedback "Failed to unmount $mountPoint"
			allDone=0
		    fi
		} | errorMessage
	    fi
	done
    else
        errorMessage "You can not unmount a non-hotpluggable device: $mountPoint."
	return
    fi

}


function scsiHostNumberFromUsbProc24 {
    # Kernel 2.4 only
    local n=`echo $1 | awk -F"[-/]" '{ n=$NF;  print n }'`
    feedback "USB proc-fs yields SCSI host number=$n"  
    echo $n
}


function partitionsFromProcfs24 {
    # Kernel 2.4 only
    local scsiUniqID="`scsiHostNumberFromUsbProc24 $1`:0:0:0"
    if [ "$scsiUniqID" == "UNKNOWN:0:0:0" ]
    then
	echo "UNKNOWN"
	return 1
    fi
    local scsiDiskDev=$(diskDevFromUniqID $scsiUniqID)
    scsiPartList=$(awk -v scsiDiskDev="$scsiDiskDev" -v nps="$NO_PARTITIONS_SUFFIX" '
       { 
         if (found)
         {
	    if ("/dev/" $4 ~ "^" scsiDiskDev) {
	       print "/dev/" $4;
               numParts++;
            }
            else 
               close("dev/stdin");
         }
         if ("/dev/" $4 == scsiDiskDev) {
            major = $1;
            found = 1;
         }
       } 
       END {
         if (numParts == 0) print scsiDiskDev nps
       }' /proc/partitions)
    feedback "Procfs partition list is $scsiPartList"
    echo "${scsiPartList}"
}


function cleanupDetachedDevices {
    local msg
    msg=0
    local mountPoint
    feedback "Cleaning up mounts for detached devices."
    if uname -r | grep -q '^2.4'
    then
	feedback "Using kernel 2.4 umount code"
    
	for procEntry in $(find /proc/scsi/ -path '/proc/scsi/usb-storage*' -type f | xargs grep -l 'Attached: No')
	do
       # some partition info will still be in procfs
	    partitions=$(partitionsFromProcfs24 $procEntry)
	    feedback "partitions=$partitions"
	    # The only safe thing to do is match the device prefix /dev/sda etc
	    # against any usb mount points - those that start with MOUNT_PATH
	    for scsiDev in $partitions; do
		for mountPoint in $(mount | awk -vscsiDev="$scsiDev" -vmountpath="$MOUNT_PATH" '$1 ~ "^" scsiDev && $3 ~ "^" mountpath { print $3 }')
		do
		    errorMessage "Please select the Unmount option from the icon on your desktop before unplugging your usb storage device or you may lose information."
		    feedback "Unmounting detached usb mount $mountPoint ($procEntry)"
		    umountDevice $mountPoint
		done
	        feedback "Cleanup has checked $scsiDev"
	    done
	done     
	if ! find /proc/scsi/ -path '/proc/scsi/usb-storage*' -type f | xargs grep -l 'Attached: Yes' 1> /dev/null
	then
	    feedback "No attached usb storage devices."
	    #feedback "No attached usb storage devices, unloading usb-module to free scsi devices."
	    [ -n "$DO_MODULE" ] && /sbin/modprobe -r usb-storage
	fi
    else
        # Linux 2.6
	feedback "Using kernel 2.6 umount code"
	local msg
	msg=0
        # look for any usb MOUNT_PATH mounts that no longer have a
        # listing in sg_map - eg if /dev/sda1 is mounted check that
        # there is a sg_map entry that covers /dev/sda.
	for mountPoint in `mount | awk 'BEGIN { while (("'$SGMAP'" | getline) > 0) sg_map[$2] = $1 } $3 ~ "'$MOUNT_PATH'" { found=0; for (d in sg_map) { if ($1 ~ d) {found=1; break} }; if (!found) print $3; }'`
	do
	    feedback "Umounting detached $mountPoint"
	    if [ $msg == 0 ] 
	    then 
		errorMessage "Please select the Unmount option from the icon on your desktop before unplugging your usb storage device or you may lose information."
		msg=1
	    fi
	    feedback "Unmounting detached usb storage device $scsiDev $mountPoint ($procEntry)"
	    umountDevice $mountPoint 
	done
    fi
}

function findUnusedMountBasename {
    local mountBasename=""
    while true
    do
        mountBasename=$MOUNT_PATH/device-$MOUNT_NUMBER
	feedback "Checking $mountBasename"
        if ! [ -n "$(mount | egrep "$mountBasename")" ]
	then
            break
        fi
        MOUNT_NUMBER=$[MOUNT_NUMBER + 1]
    done
    [ -n "$mountBasename" ] || errorExit "Failed to find a mount point basename"
    feedback "Mountpoint $mountBasename is free"
    echo "$mountBasename"
}

function cleanUnusedDirs {
    local mp
    feedback "Checking for unused mountpoints"
    for mountBasename in $MOUNT_PATH/*
    do
	feedback "Checking $mountBasename"
	if [ -d "$mountBasename" ]
	then
	    if ! [ -n "$(mount | egrep "$mountBasename")" ]
            then
                feedback "Removing $mountBasename"
		local user=$(getConsoleUser)
		alterDesktopIcon umount $mountBasename $user
		deleteDesktopIcon umount $mountBasename $user
		for part in $mountBasename/partition-[0-9]
		do
		    rmdir $part
		done
		[ -f $mountBasename/.directory ] && rm $mountBasename/.directory
		rmdir $mountBasename
	    fi
	fi
    done
}

function getConsoleUser {
    local user
    if [ -n "$DO_CONSOLE_USER" -o -n "$USE_WHO" ]
    then            
	if [ -n "$USE_WHO" ]
	then
	    user="$(who | awk '$2 == ":0" {print $1;}')"
            feedback "who reports $user on :0"
	else
            user="$(ls -l /dev/console | awk '{print $3}')"
            feedback "/dev/console user is $user"
        fi
        if [ -z "$user" ] 
        then
            feedback "Failed to obtain user for /dev/console using root instead"
            user=root
        fi
        feedback "Desktop usb user is assumed to be $user"
    else
        user="$USB_USER"
        feedback "Using USB_USER $USB_USER"
    fi
    echo "$user"
}

function desktopFilename {
    local user="$1"
    local name="$(basename $2)"
    local home="$(eval echo ~$user)"
    echo "$home/Desktop/usb-storage-$name.desktop"
}

function makeDirectoryIcon {

   [ -n "$DO_DIRECTORY_ICON" ] || return

   local mountBasename=$1
   local icon=$2
   local dirFile="$mountBasename/.directory" 
   if [ ! -f $dirFile ] 
   then
     feedback "Creating directory icon $dirFile"
     cat > "$dirFile" <<-EOF
[Desktop Entry]
Icon=$MOUNT_ICON_NAME
Type=Directory
Actions=Unmount

[Desktop Action Unmount]
Exec=/usr/local/bin/usb-umount $mountBasename
Name=Unmount
EOF
   fi

}


function makeDesktopFile {
    
    [ -n "$DO_DESKTOP" ] || return

    local mountBasename=$1
    local user=$2
    local device=$3
    local name="$(basename $mountBasename)"
    local desktopFile="$(desktopFilename $user $mountBasename)"
    if [ ! -f $desktopFile ] 
    then
        feedback "Creating desktop file $desktopFile"
        home=$(eval echo ~$user)
	[ -n "$DESKTOP_SIGNALS" ] && extraactions="UnmountSignal;UnmountKill;"
        cat > "$desktopFile" <<-EOF
[Desktop Action Unmount]
Exec=/usr/local/bin/usb-umount $mountBasename
Name=Unmount

[Desktop Action UnmountSignal]
Exec=/usr/local/bin/usb-umount -signal $mountBasename
Name=Unmount (signal processes)

[Desktop Action UnmountKill]
Exec=/usr/local/bin/usb-umount -kill $mountBasename
Name=Unmount (kill processes)

[Desktop Action MountAll]
Exec=/usr/local/bin/usb-mount all 
Name=Mount all

[Desktop Entry]
Name=usb $name
Actions=Unmount;${extraactions}MountAll
Encoding=UTF-8
Icon=usb
UnmountIcon=usb_unmount
Type=Link
Dev=$device
URL=file:$mountBasename
X-KDE-Dynamic-Device=true
EOF
       chown $user.$USB_GROUP $desktopFile
       chmod u+w $desktopFile	
    fi
}

function alterDesktopIcon {

    [ -n "$DO_DESKTOP" ] || return
    
    local action=$1
    local mountBasename=$2
    local user=$3
    local icon=$4
    local desktopFile="$(desktopFilename $user $mountBasename)"
    feedback "alter $desktopFile"
    if [ -f $desktopFile ]; then
      case "$action" in
          mount)
                  icon="$MOUNT_ICON_NAME"
                  ;;
          umount)
                  icon="$UMOUNT_ICON_NAME"
                  ;;
      esac
      feedback "Altering $desktopFile icon to $icon"
      tmpfile=$(dirname $desktopFile)/.$(basename $desktopFile).old
      mv $desktopFile $tmpfile
      awk -vicon=$icon '
              { sub("^Icon=.*$", "Icon=" icon, $0); 
                print $0; } ' $tmpfile > $desktopFile
      rm $tmpfile
      chown $user.$USB_GROUP $desktopFile
      chmod u+w $desktopFile	
    fi
}

function deleteDesktopIcon {

    [ -n "$DELETE_ICON" ] || return
    
    local action=$1
    local mountBasename=$2
    local user=$3
    local icon=$4
    local desktopFile="$(desktopFilename $user $mountBasename)"
    feedback "Removing desktop icon $desktopFile"
    if [ -f $desktopFile ]; then
      rm $desktopFile
    fi
}


function makeUmountTrap {
    # Not needed - maybe useful one day.
    if [ -n "$DEVICE" ] 
    then 
        REMOVER="$VAR_DIR/`echo $DEVICE | sed -e 's#/#%#g'`"
        rm -f -- $REMOVER
        feedback "Installing remover $REMOVER"
        ln -s /usr/local/bin/usb-umount $REMOVER
    fi
}


function chooseMountOptions {
    local scsiDev="$1"

    feedback "Selecting mount options for $scsiDev"

    local fsOpt=""
    local fsMountTypeArg="auto"

    local fsType=`$DISKTYPE $scsiDev | awk ' /file system/ { print $1 }'`
    local fsUUID=`$DISKTYPE $scsiDev | awk ' /UUID/ { print $2 }'`         
    local fsLabel=`$DISKTYPE $scsiDev | awk ' /Volume name/ { print $3 }'`

    feedback "Filesystem is '$fsType'" 
    feedback "UUID '$fsUUID', label '$fsLabel'"

    case $fsType in 
	Ext2)
	    fsOpt=""
	    fsMountTypeArg="ext2"
			;;
        Ext3)
	    fsOpt=""
	    fsMountTypeArg="ext3"
	    ;;
        ReiserFS)
	    fsOpt=""
	    fsMountTypeArg="reiserfs"
	    ;;
        NTFS)
	    if uname -r | grep -q '^2.4'
	    then
		fsOpt="-o uid=$user,gid=$USB_GROUP,umask=$USB_UMASK,user"
	    else
		fsOpt="-o uid=$user,gid=$USB_GROUP,umask=$USB_UMASK,user,dmask=$USB_DMASK"
	    fi
	    fsMountTypeArg="ntfs"
	    ;;
        FAT16)
	    fsOpt="-o uid=$user,gid=$USB_GROUP,umask=$USB_UMASK,dmask=$USB_DMASK,quiet,user"
	    fsMountTypeArg="vfat"
	    ;;
        FAT32)
	    fsOpt="-o uid=$user,gid=$USB_GROUP,umask=$USB_UMASK,dmask=$USB_DMASK,quiet,user,shortname=mixed"
	    fsMountTypeArg="vfat"
	    ;;
        *)
	    fsOpt="-o uid=$user,gid=$USB_GROUP,umask=$USB_UMASK,dmask=$USB_DMASK,quiet,user"
	    fsMountTypeArg="auto"
	    ;;
    esac

    local mountOptions="-t $fsMountTypeArg $fsOpt $MOUNT_OPTIONS"
    feedback "Mount options: $mountOptions"
    echo "$mountOptions"
}


function mountScsiDev {
    local parentDev=$1
    local partitions=`scsiPartitions $parentDev`
    local scsiDev=""
    local numParts=`echo "$partitions" | wc -w`

    user=$(getConsoleUser)
    feedback "Owner is $user - mounting $numParts $partitions for $user"

    local count=0
    local success=0
    local mountBasename=""

    for scsiDev in $partitions; do
	feedback "Doing $scsiDev"
	if ! mount | egrep -q "^$scsiDev[[:space:]]"
	then
	    # Not needed - just modify the usb.agent to call us directly
	    # makeUmountTrap 

	    if [ $count -eq 0 ]; then
		mountBasename=$(findUnusedMountBasename) || errorExit "Failed to find a mount directory"
		mkdir -p "$mountBasename" || errorExit "Failed to create base mount point $mountBasename"
	    fi
      
	    local mountPoint=""
	    if [ $numParts -gt 1 ] 
	    then
		mountPoint="$mountBasename/partition-$count"
	    else
		mountPoint="$mountBasename"
	    fi
	    count=$[count + 1]

	    feedback "Mounting $scsiDev on $mountPoint"
	    mkdir -p "$mountPoint" || errorExit "Failed to create mount point $mountPoint"

	    local mountOptions="`chooseMountOptions $scsiDev`"

	    feedback "mount $mountOptions $scsiDev $mountPoint"
	    mount -s $mountOptions $scsiDev $mountPoint 
	    if [ $? -eq 0 ]; then
		success=$[success + 1]
		feedback "Mount $scsiDev is now mounted on $mountPoint"
	    else
		feedback "Mount error for mountpoint $mountPoint, device $scsiDev!"
		rmdir $mountPoint
	    fi
	else
	    feedback "Ignoring $scsiDev - already mounted"
	fi
    done

    if [ $success -gt 0 ]; then
	makeDesktopFile $mountBasename $user $scsiDev
	makeDirectoryIcon $mountBasename
	alterDesktopIcon mount $mountBasename $user
	feedback "Device $parentDev is now available as $mountBasename"
    fi
}


function mountAttachedDevPath {
    local scsiUniqID=`scsiUniqIDFromDevPath $1`
    if [ "$scsiUniqID" == "UNKNOWN" ]
    then
        feedback "Disk unique ID is UNKNOWN"
	echo "UNKNOWN"
	return 0
    fi
    local scsiDev="`diskDevFromUniqID $scsiUniqID`"
    debugMessage "SCSI disk device is $scsiDev"
    if [ "$scsiDev" == "UNKNOWN" ]
    then
	feedback "Could not find device for $1"
	return 0
    fi
    feedback "Mounting partitions for $scsiDev ($1)"
    mountScsiDev $scsiDev
}

function mountAllAttached {
    local scsiUniqID=""
    feedback "Find and mount all attached usb storage devices."
    for scsiUniqID in $(allUsbScsiUniqID)
    do
	local scsiDev="`diskDevFromUniqID $scsiUniqID`"
	if [ "$scsiDev" == "UNKNOWN" ]
	then
	    feedback "Could not find device for $scsiUniqID"
	fi
	feedback "mount $scsiDev $scsiUniqID"
	mountScsiDev $scsiDev
    done
}

if [ "$(id -un)" != "root" ]
then
    exec $SUDO $0 "$@"
fi

if [ ! -d "$VAR_DIR/usb.lock" ]
then
    # for the lock file
    mkdir -p "$VAR_DIR"
fi

if ! $LOCKFILE -r 4 "$VAR_DIR/usb.lock"
then
    errorMessage "Could not obtain lock on $VAR_DIR/usb.lock"
    exit 1
fi

arg="$1"
feedback "+++ Starting usb (u)mounter" "$@" "DEVPATH=$DEVPATH SCSI=$SCSI"
debugMessage '------'
env | debugMessage
debugMessage '------'
cleanupDetachedDevices | errorMessage

if [ "$id" = "usb-mount" ]
then
    if [ -n "$DEVPATH" ]
    then
	feedback "Called to mount device $DEVPATH"
        mountAttachedDevPath $DEVPATH | errorMessage
    elif [ -n "$DEVICE" ]
    then
        # Linux 2.4
	# Being called from hotplug of usb-storage - assume module is loaded.
	feedback "Called to mount device $DEVICE"
        mountAllAttached | errorMessage
    elif [ -n "$arg" -a "$arg" != "all" ]
    then
	errorExit "Called to mount $arg - can't do this yet (ever?)"
    else
	feedback "Called to mount all usb devices"
        [ -n "$DO_MODULE" ] && /sbin/modprobe usb-storage
	sleep 2
        mountAllAttached | errorMessage
    fi

elif [ "$id" = "usb-umount" ] 
then
    feedback "Called to umount:" "$@"
    requestsignal=""
    if [ "$1" = "-signal" ] 
    then
	shift
	requestsignal="signal"
    fi
    if [ "$1" = "-kill" ] 
    then
	shift
	requestsignal="kill"
    fi
    arg="$1"
    if [ -n "$arg" ]
    then
	mountPointBasename=$arg
        feedback "Unmounting $mountPointBasename"
        umountDevice $mountPointBasename $requestsignal | errorMessage
    fi
fi

/bin/rm -f "$VAR_DIR/usb.lock"
cleanUnusedDirs

# End of script
