#!/bin/dash
# vim:se ft=sh ts=4 sw=4 et tw=80 cin cino=(0,ml,\:0:
#
# Copyright (C) 2009 DaTaPaX, GPL Licensed

# halmount/halumount
#-------------
# Mounts and unmounts using hal (via dbus)
#
# More info:
#   http://people.freedesktop.org/~david/hal-spec/hal-spec.html

# ToDo
#   * Read in defaults for help display from Hal

# Version
APP_VER="0.21"

# Program name
PROG="$(basename $0)"
MOUNTNAME="halmount"
UMOUNTNAME="halumount"

mounting() { test "$PROG" != "$UMOUNTNAME"; }

# Default FS type
FSTYPE=""

# Default mount options
OPTIONS=""

# Default Destination directory
DESTDIR=""

# Default to listing devices
LIST=""

EJECT=""

usage() {
    cat <<!
HalMount/HalUMount v${APP_VER}

HalMount mounts and unmounts using hal (via dbus)

    Usage: $PROG -h|--help
           $MOUNTNAME -l
           $MOUNTNAME [-t <fstype>] [-o <options>] <device> [destdir]
           $UMOUNTNAME -l
           $UMOUNTNAME [-o <options>] <device|destdir>
    
    -h|--help           - Displays this help
    -l                  - List available devices for mounting/unmounting
    -t <fstype>         - Filesystem type (default: auto)
    -o <options>        - Mount options
    <device>            - Label, UUID or device node of device to mount (eg: /dev/sdb1)
    <destdir>           - Destination mount directory (under /media, default: <device label>)

    Examples:
    
        $MOUNTNAME /dev/sdb1
        $MOUNTNAME 'APPLE IPOD' iPod
        $UMOUNTNAME 'APPLE IPOD'
!
    exit $1
}

abort() {
    echo >&2 $2
    exit $1
}

lookup() { hal-get-property --udi "$1" --key "$2" | sed 1q; }
search() { hal-find-by-property --key "$1" --string $2; }
send()   { dbus-send "$@" >/dev/null || abort $? "ERROR: dbus-send failed"; }
is_mounted() { test "$(lookup $1 volume.is_mounted)" = true; }

while getopts ehlo:t: opt
do
    case $opt in
    e)
        EJECT=1;;
    h)
        usage 0;;
    l)
        LIST=1
        break;;
    o)
        OPTIONS="$OPTARG";;
    t)
        FSTYPE="$OPTARG";;
    '?')
        usage 1;;
    esac
done

if mounting; then
    OPTIONS="${OPTIONS},uid=$(id -u)"
fi

shift $(($OPTIND - 1))

# If listing, no mount point or destination directory will be accepted
if [ -z "$LIST" ]; then
    DEVICE="$1"; shift

    if mounting && [ $# -gt 0 ]; then
        DESTDIR="$1"; shift
    fi
fi

if [ "$#" -ne 0 ]; then
    abort 1 "ERROR: Too many arguments"
fi

if [ -n "$LIST" ]; then
    # List devices
    search info.category volume |
    while read udi; do

        info=""
        for i in fstype uuid label; do info="$info	$(lookup $udi volume.$i)"; done
        dev="$(lookup $udi block.device)"

        mounted=
        case "$(lookup $udi volume.is_mounted)" in
        false)
            mounting && echo "$dev	$info";;
        true)
            mounting || echo "$dev	$(lookup $udi volume.mount_point)	$info";;
        esac
    done

    exit 0
fi

if [ -z "$DEVICE" ]; then
    abort 2 "ERROR: No device specified"
fi

if [ ! -b "$DEVICE" ]; then
    DEVICE=$(blkid -U "$DEVICE" || blkid -L "$DEVICE")
fi

# Retrieve the UDI for this device
udi="$(search block.device "$DEVICE")"

# First, check if it's a destination dir (for unmounting)
if [ -z "$udi" ] && ! mounting; then
    # mount_point does not contain a trailing slash
    DESTDIR="${DEVICE%/}"

    udi="$(search volume.mount_point "$DESTDIR")"
fi

# No device
if [ -z "$udi" ]; then
    abort 3 "ERROR: Couldn't find device in Hal"
fi

if mounting; then
    if is_mounted $udi; then
        abort 4 "Device $DEVICE already mounted on $(lookup $udi volume.mount_point)"
    fi

    # NOTE: --print-reply req'd for blocking
    send --system --print-reply --dest=org.freedesktop.Hal \
         $udi \
         org.freedesktop.Hal.Device.Volume.Mount \
         string:"$DESTDIR" \
         string:"$FSTYPE" \
         array:string:"$OPTIONS"

    if [ "$DESTDIR" = "" ]; then
        echo "Mounted $DEVICE to $(lookup $udi volume.mount_point)"
    fi
else
    # Unmounting
    method=org.freedesktop.Hal.Device.Volume.Unmount
    if [ -n "$EJECT" ]; then
        method=org.freedesktop.Hal.Device.Volume.Eject
    elif ! is_mounted $udi; then
        abort 4 "Device $DEVICE is not currently mounted"
    fi

    # NOTE: --print-reply req'd for blocking
    send --system --print-reply --dest=org.freedesktop.Hal \
         $udi \
         $method \
         array:string:"$OPTIONS"
fi

