HAL methods at storage device level, to mount/unmount/eject volumes

Kay Sievers kay.sievers at vrfy.org
Thu Dec 8 07:49:26 PST 2005


HAL volume method interface to request privileged operations like mount, umount, eject

Every storage device which contains a recognized filesystem exposes the
methods, which can be invoked over the usual HAL D-BUS interface:
  Mount(string:path, string:options)
  UnMount()
  Eject()

In the future, this interface could be extended to provide SetLabel(),
Format() or any other useful action.

For the Mount() method, the name for the mount point directory in /media
can be specified. Also the usual otions passed to the mount call can be
passed as a single string:
  /org/freedesktop/Hal/devices/volume_uuid_3e999973_00c9_4917_9442_b7633bd95b9e.Mount("Photos", "ro,iocharset=iso8859-1")
will request the mounting of the device at /media/Photos. It will be mounted
read-only and with the specified character encoding.

The methods and the interface are defined here:
  /usr/share/hal/fdi/policy/10osvendor/20-storage-methods.fdi

Helper scripts executed with the a method invocation are located here:
  /usr/share/hal/scripts/hal-system-storage-<method-name>

The D-BUS interface is controlled by the usual bus access control, which
can be limited to locally logged-in users by specifying:

  <policy context="default">
    <deny send_interface="org.freedesktop.Hal.Device.Volume"/>
  </policy>
  <policy at_console="true">
    <allow send_interface="org.freedesktop.Hal.Device.Volume"/>
  </policy>

in:
  /etc/dbus-1/system.d/hal.conf

The invoked method helpers, usually implemented as shell scripts, will need to
check for secure operations. Even when only locally logged in users are allowed
to invoke methods, the mount script itself, must check the options passed from
the user call.

All the policy about storage handling will live in the user session only. It is
driven by an asynchronous event sequence. The desktop session will receive an
event for a new storage device, can match against the offered persisten properties of
the device or volume, lookup an already stored or compute a new moutpoint and request
the mounting of the volume by invoking the method on the hal device object.

Order of events:
################
New Media:
  new device kernel -> hal(new device) -> desktop (new udi)
  desktop (matching config against udi, request mount with udi and options from config)
  hal (create mountpoint, mount)
  kernel (mount)
  kernel (mount change event) -> hal -> desktop

Request Eject/Umount in GUI:
  desktop (request umount)
  hal (umount) - if it fails, desktop throws error
  hal (remove mountpoint if created by hal)
  kernel (umount/shutdown device)
  kernel (mount change event) -> hal -> desktop

Press EjectButton
  HAL (reads event from device) -> desktop ("EjectPressed")
  desktop (request umount)
  hal (umount) - if it fails, desktop throws error
  kernel (umount)
  kernel (mount change event) -> hal -> desktop


Mount Method Example:
#####################
  dbus-send --system --print-reply --dest=org.freedesktop.Hal \
	/org/freedesktop/Hal/devices/volume_uuid_a5ea6ca9_485c_4e0f_95b3_ea85324403e2 \
	org.freedesktop.Hal.Device.Volume.Mount string:MyDevice string:ro

mount:
   $ dbus-send --system --print-reply --dest=org.freedesktop.Hal ...
   method return sender=:1.496 -> dest=:1.629
   uint32 0

failing mount:
   $ dbus-send --system --print-reply --dest=org.freedesktop.Hal ...
   Error org.freedesktop.Hal.Device.Volume.AlreadyMounted: /media/MyDevice


UnMount Method Example:
#######################
  dbus-send --system --print-reply --dest=org.freedesktop.Hal \
	/org/freedesktop/Hal/devices/volume_uuid_a5ea6ca9_485c_4e0f_95b3_ea85324403e2 \
	org.freedesktop.Hal.Device.Volume.UnMount

umount:
   $ dbus-send --system --print-reply --dest=org.freedesktop.Hal /org/freede ...
   method return sender=:1.496 -> dest=:1.634
   uint32 0

failing umount:
   kay at pim:~/hal-mount> dbus-send --system --print-reply --dest=org.freedesktop.Hal /org/freede ...
   Error org.freedesktop.Hal.Device.Volume.NotMounted: 

Thanks,
Kay
-------------- next part --------------
<?xml version="1.0" encoding="UTF-8"?>

<deviceinfo version="0.2">
  <device>
    <match key="volume.fsusage" string="filesystem">
      <append key="info.interfaces" type="strlist">org.freedesktop.Hal.Device.Volume</append>

      <append key="org.freedesktop.Hal.Device.Volume.method_names" type="strlist">Mount</append>
      <append key="org.freedesktop.Hal.Device.Volume.method_signatures" type="strlist">ss</append>
      <append key="org.freedesktop.Hal.Device.Volume.method_execpaths" type="strlist">hal-system-storage-mount</append>

      <append key="org.freedesktop.Hal.Device.Volume.method_names" type="strlist">UnMount</append>
      <append key="org.freedesktop.Hal.Device.Volume.method_signatures" type="strlist"></append>
      <append key="org.freedesktop.Hal.Device.Volume.method_execpaths" type="strlist">hal-system-storage-unmount</append>

      <append key="org.freedesktop.Hal.Device.Volume.method_names" type="strlist">Eject</append>
      <append key="org.freedesktop.Hal.Device.Volume.method_signatures" type="strlist"></append>
      <append key="org.freedesktop.Hal.Device.Volume.method_execpaths" type="strlist">hal-system-storage-eject</append>
    </match>
  </device>
</deviceinfo>
-------------- next part --------------
#!/bin/sh -e

# Copyright (C) 2005, Kay Sievers <kay.sievers at vrfy.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2.

MOUNT_ROOT="/media"

# Check for environment variables
if [ "$HAL_PROP_BLOCK_DEVICE" == "" ] || [ "$HAL_PROP_INFO_UDI" == "" ] ; then
    echo "Missing or empty environment variable(s)." >&2
    echo "This script should be started by hald." >&2
    exit 1
fi

# check if device is already mounted
if [ "$HAL_PROP_VOLUME_IS_MOUNTED" = "true" ]; then
    echo "org.freedesktop.Hal.Device.Volume.AlreadyMounted" >&2
    echo "$HAL_PROP_VOLUME_MOUNT_POINT" >&2
    exit 1
fi

# read parameters
read GIVEN_MOUNTPOINT
read GIVEN_MOUNTOPTIONS

# if no mountpoint, get mountpoint from label
if [ "$GIVEN_MOUNTPOINT" == "" ]; then
    case "$HAL_PROP_VOLUME_LABEL" in
	*[!A-Za-z0-9_\-\+:]*)
	break
	;;
    esac
    GIVEN_MOUNTPOINT="$HAL_PROP_VOLUME_LABEL"
fi

# if no mountpoint, use default name
if [ "$GIVEN_MOUNTPOINT" == "" ]; then
    GIVEN_MOUNTPOINT="disk"
fi

# validate characters in mountpoint
case "$GIVEN_MOUNTPOINT" in
    *[!A-Za-z0-9_\-\+:]*)
	echo "org.freedesktop.Hal.Device.Volume.InvalidMountpoint" >&2
	echo "" >&2
	exit 1
	;;
esac
MOUNTPOINT="$GIVEN_MOUNTPOINT"

# pass only whitelisted mount options
if [ "$GIVEN_MOUNTOPTIONS" != "" ]; then
    MOUNTOPTIONS="nosuid,nodev,user"
    case "$GIVEN_MOUNTOPTIONS" in
	*ro*)
	    MOUNTOPTIONS="$MOUNTOPTIONS,ro"
	    ;;
    esac
    case "$GIVEN_MOUNTOPTIONS" in
	*sync*)
	    MOUNTOPTIONS="$MOUNTOPTIONS,sync"
	    ;;
    esac
    case "$GIVEN_MOUNTOPTIONS" in
	*quiet*)
	    MOUNTOPTIONS="$MOUNTOPTIONS,quiet"
	    ;;
    esac
fi

# append number to mountpoint if it already exists
if [ -e "$MOUNT_ROOT/$MOUNTPOINT" ]; then
    NUM=1;
    while [ -e "$MOUNT_ROOT/$MOUNTPOINT-$NUM" ]; do
	NUM=$(($NUM + 1))
    done
    MOUNTPOINT="$MOUNTPOINT-$NUM"
fi

# create directory and mark it for cleanup with an extended attribute
if [ ! -e "$MOUNT_ROOT/$MOUNTPOINT" ]; then
    MOUNTPOINT_CREATED=1
    mkdir "$MOUNT_ROOT/$MOUNTPOINT"
    attr -s HAL_MOUNTPOINT -V "$HAL_PROP_INFO_UDI" "$MOUNT_ROOT/$MOUNTPOINT"  > /dev/null 2>&1
fi

if [ ! -e "$MOUNT_ROOT/$MOUNTPOINT" ]; then
    echo "org.freedesktop.Hal.Device.Volume.FailedToCreateMountpoint" >&2
    echo "$MOUNT_ROOT/$MOUNTPOINT" >&2
    exit 1
fi

# mount and return status
mount -o "$MOUNTOPTIONS" -t "$HAL_PROP_VOLUME_FSTYPE" "$HAL_PROP_BLOCK_DEVICE" "$MOUNT_ROOT/$MOUNTPOINT" > /dev/null 2>&1
if [ $? -ne 0 ]; then
    if [ -n "$MOUNTPOINT_CREATED" ]; then
	rmdir "$MOUNT_ROOT/$MOUNTPOINT"
    fi
    echo "org.freedesktop.Hal.Device.Volume.MountFailed" >&2
    echo "$MOUNT_ROOT/$MOUNTPOINT" >&2
    exit 1
fi

exit 0
-------------- next part --------------
#!/bin/sh -e

# Copyright (C) 2005, Kay Sievers <kay.sievers at vrfy.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2.

# Check for environment variables
if [ "$HAL_PROP_BLOCK_DEVICE" == "" ] || [ "$HAL_PROP_INFO_UDI" == "" ] ; then
    echo "Missing or empty environment variable(s)." >&2
    echo "This script should be started by hald." >&2
    exit 1
fi

if [ "$HAL_PROP_VOLUME_IS_MOUNTED" != "true" ]; then
    echo "org.freedesktop.Hal.Device.Volume.NotMounted" >&2
    echo "$HAL_PROP_VOLUME_MOUNT_POINT" >&2
    exit 1
fi

umount "$HAL_PROP_VOLUME_MOUNT_POINT" > /dev/null 2>&1 || true
if [ $? -ne 0 ]; then
    echo "org.freedesktop.Hal.Device.Volume.UnMountFailed" >&2
    echo "$HAL_PROP_VOLUME_MOUNT_POINT" >&2
    exit 1
fi

# remove directory only if HAL has created it
attr -q -g HAL_MOUNTPOINT "$HAL_PROP_VOLUME_MOUNT_POINT" > /dev/null 2>&1 || exit 0
rmdir --ignore-fail-on-non-empty "$HAL_PROP_VOLUME_MOUNT_POINT"

exit 0
-------------- next part --------------
#!/bin/sh

# Copyright (C) 2005, Kay Sievers <kay.sievers at vrfy.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2.
#
# Check for environment variables
if [ "$HAL_PROP_BLOCK_DEVICE" == "" ] || [ "$HAL_PROP_INFO_UDI" == "" ] ; then
    echo "Missing or empty environment variable(s)." >&2
    echo "This script should be started by hald." >&2
    exit 1
fi

eject "$HAL_PROP_BLOCK_DEVICE" > /dev/null 2>&1
if [ $? -ne 0 ]; then
    echo "org.freedesktop.Hal.Device.Volume.EjectFailed" >&2
    echo "$HAL_PROP_BLOCK_DEVICE" >&2
    exit 1
fi

exit 0


More information about the hal mailing list