#!/bin/sh
# This script mounts USB mass storage devices when they are plugged in
# and unmounts them when they are removed.
# Copyright © 2004, 2005 Martin Dickopp
# Copyright © 2008, 2009, 2010 Rogério Theodoro de Brito
#
# This file is free software; the copyright holder gives unlimited
# permission to copy and/or distribute it, with or without
# modifications, as long as this notice is preserved.
#
# This file is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
#
set -e
exec > /dev/null 2>&1
if [ -z "$nobg" ]; then
    nobg=true /usr/share/usbmount/usbmount $1 & 
    exit 0
fi

######################################################################
# Auxiliary functions

# Log a string via the syslog facility.
log()
{
    if [ $1 != debug ] || expr "$VERBOSE" : "[yY]" > /dev/null; then
	logger -p user.$1 -t "usbmount[$$]" -- "$2"
    fi
}


# Test if the first parameter is in the list given by the second
# parameter.
in_list()
{
    for v in $2; do
	[ "$1" != "$v" ] || return 0
    done
    return 1
}

clean_up() {
    log info "cleaning up"
    for f in $(ls -d $MNTPNTDIR/*); do [ ! $(mountpoint -q $f) ] && rmdir $f || true; done
}

udisks_load() {
    ! pgrep udisks-daemon >/dev/null || return 0
    test -x /usr/lib/udisks/udisks-daemon || return 1
    /usr/lib/udisks/udisks-daemon &
    if $(timeout 10s sh -c "while test -z \"$(ps ax| grep -e \"udisks-daemon.*polling.*\"| grep -v grep)\"; do sleep 1; done"; echo $?) -eq 124; then 
        log err "couldn't start udisks daemon"
        return 1
    fi
    
}

######################################################################
# Main program

# Default values for configuration variables.
ENABLED=1
MOUNTPOINTS=
FILESYSTEMS=
MOUNTOPTIONS=
FS_MOUNTOPTIONS=
VERBOSE=no
STARTUDISKS=0
ACTION=$1

if [ -r /etc/usbmount/usbmount.conf ]; then
    . /etc/usbmount/usbmount.conf
    log debug "loaded usbmount configurations"
fi

if [ "${ENABLED:-1}" -eq 0 ]; then
    log info "usbmount is disabled, see /etc/usbmount/usbmount.conf"
    exit 0
fi

if [ ! -x /sbin/blkid ]; then
    log err "cannot execute /sbin/blkid"
    exit 1
fi

# Per Policy 9.3.2, directories under /var/run have to be created
# after every reboot.
if [ ! -e /var/run/usbmount ]; then
    mkdir -p /var/run/usbmount
    log debug "creating /var/run/usbmount directory"
fi

# Acquire lock.
log debug "trying to acquire lock /var/run/usbmount/.mount.lock"
lockfile-create --retry 100 /var/run/usbmount/.mount || \
    { log err "cannot acquire lock /var/run/usbmount/.mount.lock"; exit 1; }
trap '( lockfile-remove /var/run/usbmount/.mount; touch /var/run/usbmount/.active; log debug "usbmount execution finished"; )' 0
log debug "acquired lock /var/run/usbmount/.mount.lock"

umask 022

log info "--> running action $ACTION on device $DEVNAME"

if [ "$ACTION" != add -a "$ACTION" != remove -a "$ACTION" != spindown ]; then
        ACTION=add
        DEVNAME=$1
fi

[ "$STARTUDISKS" -eq '1' -o "$STARTUDISKS" = "yes" ] && udisks_load

if [ "$ACTION" = add ]; then

    # Grab device information from device and "divide it"
    #   FIXME: improvement: implement mounting by label (notice that labels
    #   can contain spaces, which makes things a little bit less comfortable).
    DEVINFO="$(/sbin/blkid -p $DEVNAME)"
    FSTYPE=$(echo "$DEVINFO" | sed 's/.*[[:blank:]]TYPE="\([^"]*\)".*/\1/g; s/[[:blank:]]*//g;')
    UUID=$(/sbin/blkid -p -s UUID -o value $DEVNAME)
    USAGE=$(echo "$DEVINFO"  | sed 's/.*[[:blank:]]USAGE="\([^"]*\)".*/\1/g; s/[[:blank:]]*//g;')
    LABEL=$(/sbin/blkid -p -s LABEL -o value $DEVNAME)

    # check btrfs multidisk arrays already mounted
    amnt=no
    if [ "$FSTYPE" = btrfs ]; then
        while ! btrfs dev ready $DEVNAME; do sleep 1; done
        if [ "$(btrfs fi show $DEVNAME | grep -c devid)" -ne 1 ]; then
            for d in $(btrfs fi show $DEVNAME | grep devid | awk '{print $NF}'); do
                findmnt $d && amnt=yes || :
            done
        fi
    fi
    if test $amnt = yes || findmnt $DEVNAME > /dev/null; then
        log info "$DEVNAME already mounted. exiting"
        exit 0
    fi

    if test $FSTYPE = swap; then
        prio=0
        if test -n "$UUID" && grep "^[[:blank:]]*UUID=$UUID" /etc/fstab | grep swap | grep -q 'pri='; then
            prio=$(grep "^[[:blank:]]*UUID=$UUID" /etc/fstab | grep swap | grep 'pri='| awk '{print $4}')
        elif egrep -q "^[[:blank:]]*$DEVNAME" /etc/fstab| grep swap | grep -q 'pri='; then
            prio=$(egrep -q "^[[:blank:]]*$DEVNAME" /etc/fstab| grep swap | grep 'pri='|awk '{print $4}')
        fi
        prio=${prio%,*}; prio=${prio#*pri=}

        if [ $USESWAPS = yes ]; then
            swapon -p $prio $DEVNAME
            log info "$DEVNAME activated swap"
        fi
        exit 0
    elif ! echo $USAGE | egrep -q "(filesystem|disklabel)"; then
	log info "$DEVNAME does not contain a filesystem or disklabel"
	exit 1
    fi

    # Try to use specifications in /etc/fstab first.
    if egrep -q "^[[:blank:]]*$DEVNAME" /etc/fstab; then
	mountpoint=$(grep -e "^[[:blank:]]*$DEVNAME" /etc/fstab | awk '{print $2}')
	initctl emit -n mounting MOUNTPOINT=$mountpoint
	log info "executing command: mount $DEVNAME"
	mount $DEVNAME || log err "mount by DEVNAME with $DEVNAME wasn't successful; return code $?"

    elif test -n "$UUID" && grep -q "^[[:blank:]]*UUID=$UUID" /etc/fstab; then
        log info "executing command: mount -U '$UUID'"
	mountpoint=$(grep "^[[:blank:]]*UUID=$UUID" /etc/fstab | awk '{print $2}')
	initctl emit -n mounting MOUNTPOINT=$mountpoint
	[ -d "$mountpoint" ] || mkdir -p "$mountpoint"
	mount -U $UUID || log err "mount by UUID with $UUID wasn't successful; return code $?"

    elif [ "$(grep -c "^[[:blank:]]*LABEL=$LABEL" /etc/fstab)" -eq 1 -a -n "$LABEL" ]; then
        log info "executing command: mount -L '$LABEL'"
	mountpoint=$(grep "^[[:blank:]]*LABEL=$LABEL" /etc/fstab | awk '{print $2}')
	initctl emit -n mounting MOUNTPOINT=$mountpoint
	[ -d "$mountpoint" ] || mkdir -p "$mountpoint"
	mount -L $LABEL || log err "mount by LABEL with $LABEL wasn't successful; return code $?"

    else
	log debug "$DEVNAME contains filesystem type $FSTYPE"

	fstype=$FSTYPE
	# Test if the filesystem type is in the list of filesystem
	# types to mount.
	if in_list "$fstype" "$FILESYSTEMS"; then
	    # Search an available mountpoint.
	    if [ true ]; then
		# Determine mount options.
		options=
		for v in $FS_MOUNTOPTIONS; do
		    if expr "$v" : "-fstype=$fstype,."; then
			options="$(echo "$v" | sed 's/^[^,]*,//')"
			break
		    fi
		done
		if [ -n "$MOUNTOPTIONS" ]; then
		    options="$MOUNTOPTIONS${options:+,$options}"
		fi

		[ "$UUIDNAME" = yes ] || { [ -z "$LABEL" ] || UUID=''; }

		if test -n "$UUID" && test -n "$LABEL"; then
			mountpoint="${LABEL}-${UUID}"
		else
			mountpoint="$LABEL$UUID"
		fi

		counter=""
		while { test -d "$MNTPNTDIR/$mountpoint$counter" && mountpoint -q "$MNTPNTDIR/$mountpoint$counter"; }; do [ -z "$counter" ] && counter='0'; counter=$(($counter + 1)); done
		[ ! -d "$MNTPNTDIR/$mountpoint$counter" ] && mkdir -p "$MNTPNTDIR/$mountpoint$counter"
		[ -e "/var/run/usbmount/$mountpoint$counter" ] || touch "/var/run/usbmount/$mountpoint$counter"

		initctl emit -n mounting MOUNTPOINT="$MNTPNTDIR/$mountpoint$counter" TYPE=$FSTYPE DEVICE=$DEVNAME
		# Mount the filesystem.
		log info "executing command: mount -t$fstype ${options:+-o$options} $DEVNAME $MNTPNTDIR/$mountpoint$counter"
		if ! mount "-t$fstype" "${options:+-o$options}" "$DEVNAME" "$MNTPNTDIR/$mountpoint$counter"; then
			rmdir "$MNTPNTDIR/$mountpoint$counter"
			rm -f "/var/run/usbmount/$mountpoint$counter"
			exit 0
		fi

		# Determine vendor and model.
		vendor=
		if [ -r "/sys$DEVPATH/device/vendor" ]; then
		    vendor="`cat \"/sys$DEVPATH/device/vendor\"`"
		elif [ -r "/sys$DEVPATH/../device/vendor" ]; then
		    vendor="`cat \"/sys$DEVPATH/../device/vendor\"`"
		elif [ -r "/sys$DEVPATH/device/../manufacturer" ]; then
		    vendor="`cat \"/sys$DEVPATH/device/../manufacturer\"`"
		elif [ -r "/sys$DEVPATH/../device/../manufacturer" ]; then
		    vendor="`cat \"/sys$DEVPATH/../device/../manufacturer\"`"
		fi
		vendor="$(echo "$vendor" | sed 's/^[[:blank:]]\+//; s/[[:blank:]]\+$//')"

		model=
		if [ -r "/sys$DEVPATH/device/model" ]; then
		    model="`cat \"/sys$DEVPATH/device/model\"`"
		elif [ -r "/sys$DEVPATH/../device/model" ]; then
		    model="`cat \"/sys$DEVPATH/../device/model\"`"
		elif [ -r "/sys$DEVPATH/device/../product" ]; then
		    model="`cat \"/sys$DEVPATH/device/../product\"`"
		elif [ -r "/sys$DEVPATH/../device/../product" ]; then
		    model="`cat \"/sys$DEVPATH/../device/../product\"`"
		fi
		model="$(echo "$model" | sed 's/^[[:blank:]]\+//; s/[[:blank:]]\+$//')"

		# Run hook scripts; ignore errors.
		export UM_DEVICE="$DEVNAME"
		export UM_FILESYSTEM="$fstype"
		export UM_MOUNTOPTIONS="$options"
		export UM_VENDOR="$vendor"
		export UM_MODEL="$model"
		mountpoint="$MNTPNTDIR/$mountpoint$counter"
	    else
		# No suitable mount point found.
		log warning "no mountpoint found for $DEVNAME"
		exit 1
	    fi
	fi
    fi

    export UM_MOUNTPOINT="$mountpoint"
    initctl emit -n mounted MOUNTPOINT="$mountpoint"

    log info "executing command: run-parts /etc/usbmount/mount.d"
    run-parts /etc/usbmount/mount.d || :

elif [ "$ACTION" = remove ]; then

    # A block or partition device has been removed.
    # Test if it is mounted.
    findmnt -S "$DEVNAME"  -n -o fstype,target -r | sed 's/\\x20/ /g' | sort -k2r \
    | while read fstype mountpoint; do
	    # If the mountpoint and filesystem type are maintained by
	    # this script, unmount the filesystem.
	    if in_list "$fstype" "fuseblk $FILESYSTEMS"; then
		# Run hook scripts; ignore errors.
		export UM_DEVICE="$DEVNAME"
		export UM_MOUNTPOINT="$mountpoint"
		export UM_FILESYSTEM="$fstype"
		log info "executing command: run-parts /etc/usbmount/pre-umount.d"
		run-parts /etc/usbmount/pre-umount.d || :

		log info "executing command: umount $mountpoint"
		timeout=0
		mountpoint -q "$mountpoint" && while ! umount "$mountpoint" > /dev/null 2>&1; do sleep $timeout; timeout=$(($timeout+1)); [ $timeout -gt 10 ] && break; done || true
		! mountpoint -q "$mountpoint" || umount -l "$mountpoint" || :
		bs=$(basename "$mountpoint")
		if [ -e "/var/run/usbmount/$bs" ]; then
			rmdir "$mountpoint"
			rm "/var/run/usbmount/$bs"
		fi

		# Run hook scripts; ignore errors.
		export UM_DEVICE="$DEVNAME"
		export UM_MOUNTPOINT="$mountpoint"
		export UM_FILESYSTEM="$fstype"
		log info "executing command: run-parts /etc/usbmount/umount.d"
		run-parts /etc/usbmount/umount.d || :
	    fi
    done
elif [ "$ACTION" = spindown ]; then

    [ "$SPINDOWN" -gt 0 ] || exit 0

    # check if this is device or just partition
    p=$(udevadm info --query=path --name=$DEVNAME)
    if [ ! -e /sys/$p/device ]; then
        log info "$DEVNAME is partition. not setting spindown parameters"
        return 0
    fi

#    hdparm -B $DEVNAME | grep "not supported" && exit 0 || :
    [ "$SPINDOWN" -gt 20 ] && SPINDOWN=20 || :
    SPINDOWN=$((SPINDOWN*60/5))
    log info "setting hd spindown (executing command: hdparm -S$SPINDOWN $DEVNAME)"
    hdparm -S$SPINDOWN $DEVNAME || :
else
    log err "unexpected: action '$1'"
    exit 1
fi

