I used the standard out-of-the-box version of Ruby which comes with Fedora 11, i.e.
$ ruby --version
ruby 1.8.6 (2009-06-08 patchlevel 369) [x86_64-linux]
Those readers who are familiar with Ruby will recognize that this version of Ruby is quite old by Ruby standards and is in maintenance mode with Kirk Haines of Engine Yard being the lead maintainer. Hopefully a 1.9 version of Ruby will be included in the official repositories for Fedora 12.
I also installed the latest version (0.2.9 "I'm not dead") of ruby-dbus which comes as a compressed tarball rather than as a gem. This project was recently taken over by Martin Vidner, who works for Novell in their Prague development group, after a long period of no activity by the original developers. An introduction to and a tutorial on ruby-dbus is available here. It needs to be updated and expanded to include all the interfaces and methods but it is a good introduction to the general concepts.
Introspection is core to D-Bus scripting. Here is one way to introspect the D-Bus system bus and output the resulting introspection data in XML format. The introspection data format is specified by the D-Bus Specification. The introspect_data method does all the heavy lifting.
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SystemBus.instance
xml = bus.introspect_data("org.freedesktop.DBus", "/org/freedesktop/DBus/Introspectable")
puts xml
Here is the output from my computer which is running Fedora 11:
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.DBus.Introspectable">
<method name="Introspect">
<arg name="data" direction="out" type="s"/>
</method>
</interface>
<interface name="org.freedesktop.DBus">
<method name="Hello">
<arg direction="out" type="s"/>
</method>
<method name="RequestName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="ReleaseName">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="StartServiceByName">
<arg direction="in" type="s"/>
<arg direction="in" type="u"/>
<arg direction="out" type="u"/>
</method>
<method name="UpdateActivationEnvironment">
<arg direction="in" type="a{ss}"/>
</method>
<method name="NameHasOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="b"/>
</method>
<method name="ListNames">
<arg direction="out" type="as"/>
</method>
<method name="ListActivatableNames">
<arg direction="out" type="as"/>
</method>
<method name="AddMatch">
<arg direction="in" type="s"/>
</method>
<method name="RemoveMatch">
<arg direction="in" type="s"/>
</method>
<method name="GetNameOwner">
<arg direction="in" type="s"/>
<arg direction="out" type="s"/>
</method>
<method name="ListQueuedOwners">
<arg direction="in" type="s"/>
<arg direction="out" type="as"/>
</method>
<method name="GetConnectionUnixUser">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetConnectionUnixProcessID">
<arg direction="in" type="s"/>
<arg direction="out" type="u"/>
</method>
<method name="GetAdtAuditSessionData">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</method>
<method name="GetConnectionSELinuxSecurityContext">
<arg direction="in" type="s"/>
<arg direction="out" type="ay"/>
</method>
<method name="ReloadConfig">
</method>
<method name="GetId">
<arg direction="out" type="s"/>
</method>
<signal name="NameOwnerChanged">
<arg type="s"/>
<arg type="s"/>
<arg type="s"/>
</signal>
<signal name="NameLost">
<arg type="s"/>
</signal>
<signal name="NameAcquired">
<arg type="s"/>
</signal>
</interface>
</node>
The following example uses the introspect method instead of the introspect_data method to enumerate a list of the available services using the ListActivatableNames method. I am going to assume that you are familiar with the Ruby language.so concepts such as looping and arrays do not need explanation.
require 'dbus'
bus = DBus::SystemBus.instance
proxy = bus.introspect("org.freedesktop.DBus", "/org/freedesktop/DBus/ListActivatableNames")
bus.proxy.ListActivatableNames[0].each do |service|
puts "#{service}"
end
This is the output:
org.freedesktop.DBus
org.freedesktop.DeviceKit.Disks
org.fedoraproject.Setroubleshootd
com.hp.hplip
org.fedoraproject.Config.Services
org.freedesktop.ConsoleKit
org.gnome.CPUFreqSelector
net.reactivated.Fprint
org.freedesktop.PackageKit
org.freedesktop.DeviceKit
org.freedesktop.NetworkManagerSystemSettings
org.gnome.ClockApplet.Mechanism
org.kerneloops.submit
org.freedesktop.PolicyKit
org.freedesktop.Gypsy
org.gnome.GConf.Defaults
fi.epitest.hostap.WPASupplicant
org.gnome.SystemMonitor.Mechanism
org.freedesktop.DeviceKit.Power
org.freedesktop.nm_dispatcher
org.opensuse.CupsPkHelper.Mechanism
You can query devices via the D-Bus interface to HAL as the following examples demonstrate.
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SystemBus.instance
xml = bus.introspect_data("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer")
puts xml
Here is what is outputted when this script is executed:
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node>
<interface name="org.freedesktop.Hal.Device">
<method name="GetAllProperties">
<arg name="properties" direction="out" type="a{sv}"/>
</method>
<method name="SetMultipleProperties">
<arg name="properties" direction="in" type="a{sv}"/>
</method>
<method name="GetProperty">
<arg name="key" direction="in" type="s"/>
<arg name="value" direction="out" type="v"/>
</method>
......
<signal name="InterfaceLockAcquired">
<arg name="interface_name" type="s"/>
<arg name="lock_holder" type="s"/>
<arg name="num_locks" type="i"/>
</signal>
<signal name="InterfaceLockReleased">
<arg name="interface_name" type="s"/>
<arg name="lock_holder" type="s"/>
<arg name="num_locks" type="i"/>
</signal>
</interface>
.......
<interface name="org.freedesktop.Hal.Device.CPUFreq">
<method name="SetCPUFreqGovernor">
<arg name="governor_string" direction="in" type="s"/>
</method>
<method name="SetCPUFreqPerformance">
<arg name="value" direction="in" type="i"/>
</method>
<method name="SetCPUFreqConsiderNice">
<arg name="value" direction="in" type="b"/>
</method>
<method name="GetCPUFreqGovernor">
<arg name="return_code" direction="out" type="s"/>
</method>
<method name="GetCPUFreqPerformance">
<arg name="return_code" direction="out" type="i"/>
</method>
<method name="GetCPUFreqConsiderNice">
<arg name="return_code" direction="out" type="b"/>
</method>
<method name="GetCPUFreqAvailableGovernors">
<arg name="return_code" direction="out" type="as"/>
</method>
</interface>
</node>
Here is an example of how to output the previous data in a more readable form using the interfaces method..
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SystemBus.instance
proxy = bus.introspect("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer")
proxy.interfaces.each do |interface|
puts "Interface: #{interface}"
proxy[interface].methods.each do |key,value|
puts " Method: #{key}"
end
proxy[interface].signals.each do |key,value|
puts " Signal: #{key}"
end
Here is an portion of the output: from executing this script.
Interface: org.freedesktop.Hal.Device.CPUFreq
Method: GetCPUFreqConsiderNice
Method: GetCPUFreqGovernor
Method: SetCPUFreqPerformance
Method: GetCPUFreqAvailableGovernors
Method: SetCPUFreqGovernor
Method: GetCPUFreqPerformance
Method: SetCPUFreqConsiderNice
Interface: org.freedesktop.Hal.Device.SystemPowerManagement
Method: Shutdown
Method: SuspendHybrid
Method: SetPowerSave
Method: Hibernate
Method: Reboot
Method: Suspend
Interface: org.freedesktop.Hal.Device
Method: ReleaseInterfaceLock
Method: ClaimInterface
Method: Reprobe
........
Method: SetPropertyBoolean
Signal: InterfaceLockAcquired
Signal: InterfaceLockReleased
Signal: PropertyModified
Signal: Condition
Interface: org.freedesktop.DBus.Introspectable
Method: Introspect
You can easily drill down to individual interfaces and methods. For example, here is one way to list the set of available CPU frequency governors.
#!/usr/bin/ruby
#
# <interface name="org.freedesktop.Hal.Device.CPUFreq">
# <method name="SetCPUFreqGovernor">
# <arg name="governor_string" direction="in" type="s"/>
# </method>
# <method name="SetCPUFreqPerformance">
# <arg name="value" direction="in" type="i"/>
# </method>
# <method name="SetCPUFreqConsiderNice">
# <arg name="value" direction="in" type="b"/>
# </method>
# <method name="GetCPUFreqGovernor">
# <arg name="return_code" direction="out" type="s"/>
# </method>
# <method name="GetCPUFreqPerformance">
# <arg name="return_code" direction="out" type="i"/>
# </method>
# <method name="GetCPUFreqConsiderNice">
# <arg name="return_code" direction="out" type="b"/>
# </method>
# <method name="GetCPUFreqAvailableGovernors">
# <arg name="return_code" direction="out" type="as"/>
# </method>
# </interface>
#
require 'dbus'
bus = DBus::SystemBus.instance
proxy = bus.introspect("org.freedesktop.Hal", "/org/freedesktop/Hal/devices/computer")
interface = proxy["org.freedesktop.Hal.Device.CPUFreq"]
governors = interface.GetCPUFreqAvailableGovernors
puts "Available CPU Freq Governors:"
governors[0].each do |gov|
puts " #{gov}"
end
which produces the following output:
Available CPU Freq Governors:
ondemand
userspace
performance
Lots of useful information can be be obtained from the HAL manager interface. The following script lists all the methods supported by the HAL manager interface and then uses the GetAllDevices method to enumerate the devices on my computer.
require 'dbus'
bus = DBus::SystemBus.instance
proxy = bus.introspect("org.freedesktop.Hal", "/org/freedesktop/Hal/Manager")
puts "HAL manager reports following methods:"
proxy.interfaces[0].each do |interface|
proxy[interface].methods.each do |key, value|
puts " #{key}"
end
end
interface = proxy["org.freedesktop.Hal.Manager"]
devices = interface.GetAllDevices
puts "\nHAL manager reports following devices:"
devices[0].each do |device|
puts " #{device}"
end
Here is a portion of the output when this script is executed
HAL manager reports following methods:
AcquireGlobalInterfaceLock
DeviceExists
CommitToGdl
GetAllDevicesWithProperties
FindDeviceByCapability
NewDevice
SingletonAddonIsReady
FindDeviceStringMatch
GetAllDevices
ReleaseGlobalInterfaceLock
Remove
HAL manager reports following devices:
/org/freedesktop/Hal/devices/net_ba_cf_03_4e_14_ca
/org/freedesktop/Hal/devices/volume_part7_size_115326976
/org/freedesktop/Hal/devices/computer
/org/freedesktop/Hal/devices/storage_model_DVD_Writer_1070d
...............
/org/freedesktop/Hal/devices/acpi_CPU0
/org/freedesktop/Hal/devices/acpi_CPU1
/org/freedesktop/Hal/devices/acpi_CPU2
/org/freedesktop/Hal/devices/acpi_CPU3
/org/freedesktop/Hal/devices/pci_8086_2940
/org/freedesktop/Hal/devices/pci_8086_293e
/org/freedesktop/Hal/devices/usb_device_1d6b_2_0000_00_1a_7_if0
/org/freedesktop/Hal/devices/usb_device_1d6b_2_0000_00_1a_7
/org/freedesktop/Hal/devices/pci_8086_293c
/org/freedesktop/Hal/devices/usb_device_1d6b_1_0000_00_1a_2_if0
/org/freedesktop/Hal/devices/usb_device_1d6b_1_0000_00_1a_2
/org/freedesktop/Hal/devices/pci_8086_2939
/org/freedesktop/Hal/devices/usb_device_1d6b_1_0000_00_1a_1_if0
/org/freedesktop/Hal/devices/usb_device_1d6b_1_0000_00_1a_1
/org/freedesktop/Hal/devices/pci_8086_2938
/org/freedesktop/Hal/devices/usb_device_1d6b_1_0000_00_1a_0_if0
/org/freedesktop/Hal/devices/usb_device_1d6b_1_0000_00_1a_0
/org/freedesktop/Hal/devices/pci_8086_2937
/org/freedesktop/Hal/devices/pci_8086_294c
/org/freedesktop/Hal/devices/pci_10de_640
/org/freedesktop/Hal/devices/pci_8086_29e1
/org/freedesktop/Hal/devices/pci_8086_29e0
The next example uses the D-Bus interface to Tomboy, a desktop note-taking application, to create a simple note, display it for 5 seconds and then delete the note.
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SessionBus.instance
service = bus.service("org.gnome.Tomboy")
tomboy = service.object("/org/gnome/Tomboy/RemoteControl")
tomboy.introspect
tomboy.default_iface = "org.gnome.Tomboy.RemoteControl"
note = tomboy.CreateNamedNote("My Note")[0]
tomboy.SetNoteContents(note, "Hello World")
tomboy.DisplayNote(note)
sleep 5
tomboy.DeleteNote(note)
Note the need to introspect the tomboy object and set the default interface using the default_iface method.
You can also add one or more tags to a Tomboy note and extract comprehensive metadata in XML format as shown in the following example.
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SessionBus.instance
service = bus.service("org.gnome.Tomboy")
tomboy = service.object("/org/gnome/Tomboy/RemoteControl")
tomboy.introspect
tomboy.default_iface = "org.gnome.Tomboy.RemoteControl"
note = tomboy.CreateNamedNote("My Note")[0]
tomboy.SetNoteContents(note, "Hello World")
tomboy.AddTagToNote(note, "blog example")
tomboy.DisplayNote(note)
sleep 5
xml = tomboy.GetNoteCompleteXml(note)
tomboy.DeleteNote(note)
puts xml
Here is the XML that is outputted when this script is executed:
<?xml version="1.0" encoding="utf-16"?>
<note version="0.3" xmlns:link="http://beatniksoftware.com/tomboy/link" xmlns:size="http://beatniksoftware.com/tomboy/size" xmlns="http://beatniksoftware.com/tomboy">
<title>My Note</title>
<text xml:space="preserve"><note-content version="0.1">Hello World</note-content></text>
<last-change-date>2009-09-07T15:44:10.2178650-04:00</last-change-date>
<last-metadata-change-date>2009-09-07T15:44:10.2212100-04:00</last-metadata-change-date>
<create-date>2009-09-07T15:44:10.2142940-04:00</create-date>
<cursor-position>37</cursor-position>
<width>450</width>
<height>360</height>
<x>1440</x>
<y>0</y>
<tags>
<tag>blog example</tag>
</tags>
<open-on-startup>False</open-on-startup>
</note>
The next example show you how to monitor D-Bus messages and act upon certain messages. You can do this using D-Bus signals. Suppose, for example, you want to monitor activity relating to Tomboy notes such as when Tomboy notes are created. First of all you need to find out what D-Bus signals Tomboy supports. This is easy to do using introspection as shown in the following script.
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SessionBus.instance
service = bus.service("org.gnome.Tomboy")
tomboy = service.object("/org/gnome/Tomboy/RemoteControl")
tomboy.introspect
tomboy.default_iface = "org.gnome.Tomboy.RemoteControl"
puts "List of supported signals:"
tomboy.signals.each do |key,value|
puts " #{key}"
end
Here is the output:
List of supported signals:
NoteSaved
NoteAdded
NoteDeleted
As you can see the version of Tomboy on my computer emits three D-Bus signals - NoteAdded when a note is added, NoteSaved when a note is saved, and NoteDeleted when a note is deleted.
The following script outputs a message each time a Tomboy note is created, saved or deleted.
#!/usr/bin/ruby
require 'dbus'
bus = DBus::SessionBus.instance
match = DBus::MatchRule.new
match.type = "signal"
match.interface = "org.gnome.Tomboy.RemoteControl"
match.path = "/org/gnome/Tomboy/RemoteControl"
bus.add_match(match) do |msg, misc|
puts "#{msg.member} #{msg.params[0]}"
end
main = DBus::Main.new
main << bus
main.run
Here is what is outputted when a single Tomboy note is created, saved and then deleted.
NoteAdded note://tomboy/56587548-7535-4e21-a4b3-3ee85ffa7756
NoteSaved note://tomboy/56587548-7535-4e21-a4b3-3ee85ffa7756
NoteDeleted note://tomboy/56587548-7535-4e21-a4b3-3ee85ffa7756
Well that's all for now. There is a lot more that you can do using ruby-dbus but I will leave it up to you to go explore the possibilities. The best place to discover what is possible is to study the ruby-dbussource code. It is fairly compact and terse but logically laid out.
0 comments:
Post a Comment