Mad, Beautiful Ideas
Detecting Removable Storage Mounts using DBus and Python

As part of my workout regimen, I tend to prefer machines at the Gym that use this StarTrac system to dump data snapshots of my performance (heart rate, speed, calorie burn rate, etc.) to a binary file. In a future project, I plan to decode this file and perhaps do something with the data, but in the meantime, I'm trying to recreate the uploader function used by the eFitness website the rec center has contracted with for handling this data. That uploader is written in .NET, and takes advantage of some P/Invoke calls unique to Windows to detect when new removable media is added.

Luckily, the Web Service the website uses has a public WSDL, and it's a pretty straightforward SOAP web service. However, this post isn't about all that. When collecting StarTrac data, I plug a simple USB thumb drive to a box attached to the excercise machine, it updates a file on the drive every 15 seconds. The path on the drive is easy enough to know (same folder, easy pattern to file names), but how do I detect when the user has attached the device? And where Linux mounted it?

The answer, as with just about anything communication related these days on Linux, is dbus. However, even knowing that you can get that information doesn't do much for the how. Which is why dbus-monitor is so important. Running dbus-monitor, watching the session-bus (I assume the user is logged in, since I'm using desktopcouch to store data), and on GNOME, the interesting block was this:

        string "org.gtk.Private.GduVolumeMonitor"
        string "0x822b808"
        struct {
            string "0x822b808"
            string "DISK_IMG"
            string ". GThemedIcon drive-removable-media-usb drive-removable-media drive-removable-drive"
            string ""
            string "file:///media/DISK_IMG"
            boolean true
            string "0x822c948"
            array [ ]
        }
        

This data came in on interface org.gtk.Private.RemoteVolumeMonitor, member MountAdded. I'll cover in a snippet below (which I plan to contribute to python-snippets). There is one problem I need to solve here. This will provide me with every new mount, not just new USB thumb drives. Now, I could parse the third member of that struct, but that's gtk sensitive data. It's possible to change, and would make the code potentially harder to post to KDE or others. Perhaps that last hex value string has the information I need, but I really have no idea. I don't see anything obviously useful in any of the other sets of member data that I feel I could trust...

However, listening for this event is easy:

        import dbus
        import dbus.glib # Provides the required Main Loop
        
        sessionbus = dbus.SessionBus()
        self.sessionbus.add_signal_reciever(signal_name="MountAdded",
                                            dbus_interface="org.gtk.Private.RemoteVolumeMonitor",
                                            path="/org/gtk/Private/RemoteVolumeMonitor",
                                            bus_name=None,
                                            handler_function=mountDetected)
        
        def mountDetected(sender, mount_id, data):
            print "New drive mounted at %s" % data[4][7:]
        

I probably don't need to put the Interface and the Path, but I'm a total newbie to dbus, so I did for completeness. And my only issue is that I will see CD's mounted using this as well, but I suppose I just have to hope they don't have the folder I'm looking for...but hopefully I can find a better way, even if it involved looking at multiple dbus events and do some internal correlation.