Freitag, 7. Mai 2010

dbus tutorial - part 3

The last part started with the basic structure of the dbus part of the CD file listing utility and then showed the particular problems with the HAL implementation.

DeviceKit.Disks has to overcome similar problems, but due to the different concepts behind DeviceKit they have to be solved slightly differently.

DeviceKit.Disks

Like HAL, DeviceKit.Disks is another daemon handling devices and is offering similar services under different names. HAL is a jack of all trades, providing information about nearly everything in your system. This lead to its downfall, because the code base became huge and unmaintainable.  DeviceKit on the other hand is split into individual more specialised daemons.  DeviceKit.Disks is strictly focussed on disks and drives.

Differences to HAL from a programmers point of view:
  • As already seen: A different function is used to install the callbacks.
  • The string given to the callback function (e.g. /org/freedesktop/DeviceKit/Disks/devices/sr0)  is the name of a Devkit device. It points to a hardware device (e.g. the CDROM drive) not a volume (the inserted medium).  And because the string already points to the hardware device we don't have to check for any parent object.
  • Being a fixed drive, we also don't need the device_added_callback or device_removed_callback, because the physical CDROM device is not likely to be removed during the lifetime of the program and only changing the medium does not trigger these callbacks. (Note: A USB stick where the physical device itself is being unplugged does trigger these callbacks.).
  • Which leaves the device_changed_callback. However, unlike HAL, there is no indication which property has changed when it is being triggered. We have to track the appropriate properties ourselves.
Initialization
(See part 2)

import gobject
import dbus
...
gobject.threads_init()
dbus.glib.threads_init()


Adding the callback
(See part 1)

bus = dbus.SystemBus()
devkit_object = bus.get_object("org.freedesktop.DeviceKit.Disks",
    "/org/freedesktop/DeviceKit/Disks")
devkit_disks = dbus.Interface(devkit_object, 'org.freedesktop.DeviceKit.Disks')
devkit_disks.connect_to_signal('DeviceChanged', device_changed_callback)

...

def device_changed_callback(device):
    print 'Device %s was changed' % (device)


Device properties in Devicekit.Disks

DeviceKit.Disks devices implement an interface named Properties which gives access to the device properties:

device_object = bus.get_object("org.freedesktop.DeviceKit.Disks", udi)
device_prop = dbus.Interface(device_object, "org.freedesktop.DBus.Properties")
property = device_prop.Get("org.freedesktop.DeviceKit.Disks.Device","name_of_property")

# or

all_properties = device_prop.GetAll("org.freedesktop.DeviceKit.Disks.Device")


Interesting properties are:
  • DriveMediaCompatibility
    description: hardware compatibility
    typical values: optical_cd, optical_cd_r, optical_cd_rw, optical_dvd, optical_dvd_r,  optical_dvd_ram
  • DeviceIsOpticalDisc
    typical values: 0/1
    0: disk tray empty
    1: optical media has been found
  • DeviceIsMounted
    typical values: 0/1
    0: nothing mounted yet
    1: valid DeviceMountPaths
  • IdLabel
    description: label of CD/DVD
  • DeviceMountPaths
    descirption: mount point, if a CD/DVD has been mounted
  • OpticalDiscIsBlank
    typical values: 0/1
    0: media in the drive is not blank
    1: media in the drive is blank
  • DeviceIsRemovable
    typical values: 0/1
    1: device can contain removable media (-> Eject should work)
See also: http://hal.freedesktop.org/docs/DeviceKit/

You can get a quick overview with human readable property names with

devkit-disks --dump | more

How to filter out events that are not CD related

The most promising way is to check DriveMediaCompatibility for the value optical_cd or optical_dvd.

device_object = bus.get_object("org.freedesktop.DeviceKit.Disks", udi)
device_prop = dbus.Interface(device_object, "org.freedesktop.DBus.Properties")
md = device_prop.Get("org.freedesktop.DeviceKit.Disks.Device","DriveMediaCompatibility")
print "is CD-ROM:", (u"optical_cd" in md) or (u"optical_dvd" in md)


If you observe the events (using the dbus-monitor) when inserting or ejecting a CD, the following patterns emerge:
(I haven't found any authoritative documentation yet, but the steps seem logical):
  • When the CDROM tray is empty:
    DeviceIsOpticalDisc == 0
  • Blank medium inserted:
    DeviceIsOpticalDisc == 1
    OpticalDiscIsBlank == 1
  • After ejecting the blank medium:
    DeviceIsOpticalDisc == 0

  • Insert a medium containing data
    • step 1 (medium gets recognized):
      DeviceIsOpticalDisc == 1
      OpticalDiscIsBlank == 0
      DeviceIsMounted == 0
    • step 2 (medium gets auto mounted):
      DeviceIsOpticalDisc == 1
      OpticalDiscIsBlank == 0
      DeviceIsMounted == 1
  • eject medium:
    • step 1 (auto umount)
      DeviceIsOpticalDisc == 1
      OpticalDiscIsBlank == 0
      DeviceIsMounted == 0
    • step 2 (eject)
      DeviceIsOpticalDisc == 0
      OpticalDiscIsBlank == 0
      DeviceIsMounted == 0

  • If DeviceIsOpticalDisc is being set to 1, the property IdLabel becomes valid.
  • If DeviceIsMounted is being set to 1, the property DeviceMountPaths becomes valid.
When to read the file list

As DeviceKit.Disks does not provide the information which property was modified, we have to track the state of DeviceIsMounted ourselves and read the files below the mount point(s) when DeviceIsMounted changes from 0 to 1.

Eject via software

This function is located in the interface org.freedesktop.DeviceKit.Disks.Device. Note that a volume must be unmounted first before it can be ejected (see property DeviceIsMounted).

system_bus = dbus.SystemBus()
device_object = system_bus.get_object("org.freedesktop.DeviceKit.Disks", udi)

# get properties
device_prop = dbus.Interface(device_object, "org.freedesktop.DBus.Properties")
all_properties = device_prop.GetAll("org.freedesktop.DeviceKit.Disks.Device")

# call unmount/eject depending on the device state
disk_dev_func = dbus.Interface(device_object, "org.freedesktop.DeviceKit.Disks.Device")
if all_properties['DeviceIsMounted'] == 1:
    disk_dev_func.FilesystemUnmount('')
if all_properties['DeviceIsOpticalDisc'] == 1:
    disk_dev_func.DriveEject('')


With this, we have all the building blocks for DeviceKit.disks implementation of the CD file listing utility

Keine Kommentare: