ruby-dbus-0.7.2/ 0000755 0000041 0000041 00000000000 11741120355 013463 5 ustar www-data www-data ruby-dbus-0.7.2/README 0000644 0000041 0000041 00000002527 11741120355 014351 0 ustar www-data www-data = Ruby D-Bus README
Ruby D-Bus provides an implementation of the D-Bus protocol such that the
D-Bus system can be used in the Ruby programming language.
== Requirements
* Ruby 1.8.7 or 1.9
== Installation
* gem install ruby-dbus
== Feature
Ruby D-Bus currently supports the following features:
* Connecting to local buses.
* Accessing remote services, objects and interfaces.
* Invoking methods on remote objects synchronously and asynchronously.
* Catch signals on remote objects and handle them via callbacks.
* Remote object introspection.
* Walking object trees.
* Creating services and registering them on the bus.
* Exporting objects with interfaces on a bus for remote use.
* Rubyish D-Bus object and interface syntax support that automatically
allows for introspection.
* Emitting signals on exported objects.
== Usage
See some of the examples in the examples/ subdirectory of the tarball.
Also, check out the included tutorial (in Markdown format) in doc/tutorial/
or view it online on
https://github.com/mvidner/ruby-dbus/blob/master/doc/tutorial/index.markdown .
== License
Ruby D-Bus is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as published by the
Free Software Foundation; either version 2.1 of the License, or (at
your option) any later version.
ruby-dbus-0.7.2/test/ 0000755 0000041 0000041 00000000000 11741120355 014442 5 ustar www-data www-data ruby-dbus-0.7.2/test/binding_test.rb 0000755 0000041 0000041 00000003346 11741120355 017451 0 ustar www-data www-data #!/usr/bin/env ruby
# Test the binding of dbus concepts to ruby concepts
require "test/unit"
require "dbus"
class BindingTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
@base = @svc.object "/org/ruby/MyInstance"
@base.introspect
@base.default_iface = "org.ruby.SampleInterface"
end
# https://trac.luon.net/ruby-dbus/ticket/36#comment:3
def test_class_inheritance
derived = @svc.object "/org/ruby/MyDerivedInstance"
derived.introspect
# it should inherit from the parent
assert_not_nil derived["org.ruby.SampleInterface"]
end
# https://trac.luon.net/ruby-dbus/ticket/36
# Interfaces and methods/signals appeared on all classes
def test_separation_of_classes
test2 = @svc.object "/org/ruby/MyInstance2"
test2.introspect
# it should have its own interface
assert_not_nil test2["org.ruby.Test2"]
# but not an interface of the Test class
assert_nil test2["org.ruby.SampleInterface"]
# and the parent should not get polluted by the child
assert_nil @base["org.ruby.Test2"]
end
def test_translating_errors_into_exceptions
# this is a generic call that will reply with the specified error
@base.Error "org.example.Fail", "as you wish"
assert false, "should have raised"
rescue DBus::Error => e
assert_equal "org.example.Fail", e.name
assert_equal "as you wish", e.message
end
def test_generic_dbus_error
# this is a generic call that will reply with the specified error
@base.will_raise_error_failed
assert false, "should have raised"
rescue DBus::Error => e
assert_equal "org.freedesktop.DBus.Error.Failed", e.name
assert_equal "failed as designed", e.message
end
end
ruby-dbus-0.7.2/test/t3-ticket27.rb 0000755 0000041 0000041 00000001351 11741120355 016752 0 ustar www-data www-data #!/usr/bin/env ruby
# Test passing a particular struct array through a variant
# https://trac.luon.net/ruby-dbus/ticket/27
require "dbus"
session_bus = DBus::ASessionBus.new
svc = session_bus.service("org.ruby.service")
obj = svc.object("/org/ruby/MyInstance")
obj.introspect # necessary
obj.default_iface = "org.ruby.SampleInterface"
# The bug is probably alignment related so whether it triggers
# depends also on the combined length of service, interface,
# and method names. Luckily here it works out.
triple = ['a(uuu)', []]
obj.test_variant(triple)
quadruple = ['a(uuuu)', []] # a(uuu) works fine
# The bus disconnects us because of malformed message,
# code 12: DBUS_INVALID_TOO_MUCH_DATA
obj.test_variant(quadruple)
ruby-dbus-0.7.2/test/service_newapi.rb 0000755 0000041 0000041 00000014243 11741120355 020001 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
# find the library without external help
$:.unshift File.expand_path("../../lib", __FILE__)
require 'dbus'
def d(msg)
puts "#{$$} #{msg}" if $DEBUG
end
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
class Test < DBus::Object
INTERFACE = "org.ruby.SampleInterface"
def initialize(path)
super path
@read_me = "READ ME"
@read_or_write_me = "READ OR WRITE ME"
end
# Create an interface aggregating all upcoming dbus_method defines.
dbus_interface INTERFACE do
dbus_method :hello, "in name:s, in name2:s" do |name, name2|
puts "hello(#{name}, #{name2})"
end
dbus_method :test_variant, "in stuff:v" do |variant|
p variant
end
dbus_method :bounce_variant, "in stuff:v, out chaff:v" do |variant|
[variant]
end
dbus_method :variant_size, "in stuff:v, out size:u" do |variant|
[variant.size]
end
dbus_method :the_answer, "out answer:i" do
42
end
dbus_method :will_raise, "" do
raise "Handle this"
end
dbus_method :will_raise_error_failed, "" do
raise DBus.error, "failed as designed"
end
dbus_method :will_raise_name_error, "" do
"foo".frobnicate
end
dbus_method :Error, "in name:s, in description:s" do |name, description|
raise DBus.error(name), description
end
end
# closing and reopening the same interface
dbus_interface INTERFACE do
dbus_method :multibyte_string, "out string:s" do
"あいうえお"
end
dbus_signal :SomethingJustHappened, "toto:s, tutu:u"
end
dbus_interface "org.ruby.AnotherInterface" do
dbus_method :ThatsALongMethodNameIThink do
puts "ThatsALongMethodNameIThink"
end
dbus_method :Reverse, "in instr:s, out outstr:s" do |instr|
outstr = instr.split(//).reverse.join
[outstr]
end
end
dbus_interface "org.ruby.Ticket30" do
dbus_method :Sybilla, 'in choices:av, out advice:s' do |choices|
["Do #{choices[0]}"]
end
end
dbus_interface "org.ruby.Loop" do
# starts doing something long, but returns immediately
# and sends a signal when done
dbus_method :LongTaskBegin, 'in delay:i' do |delay|
# FIXME did not complain about mismatch between signature and block args
d "Long task began"
task = Thread.new do
d "Long task thread started (#{delay}s)"
sleep delay
d "Long task will signal end"
self.LongTaskEnd
end
task.abort_on_exception = true # protect from test case bugs
end
dbus_signal :LongTaskEnd
end
# Properties:
# ReadMe:string, returns "READ ME" at first, then what WriteMe received
# WriteMe:string
# ReadOrWriteMe:string, returns "READ OR WRITE ME" at first
dbus_interface PROPERTY_INTERFACE do
dbus_method :Get, "in interface:s, in propname:s, out value:v" do |interface, propname|
if interface == INTERFACE
if propname == "ReadMe"
[@read_me]
elsif propname == "ReadOrWriteMe"
[@read_or_write_me]
elsif propname == "WriteMe"
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"), "Property '#{interface}.#{propname}' (on object '#{@path}') is not readable"
else
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"), "Property '#{interface}.#{propname}' not found on object '#{@path}'"
end
else
raise DBus.error("org.freedesktop.DBus.Error.UnknownInterface"), "Interface '#{interface}' not found on object '#{@path}'"
end
# what should happen for unknown properties
# plasma: InvalidArgs (propname), UnknownInterface (interface)
end
dbus_method :Set, "in interface:s, in propname:s, in value:v" do |interface, propname, value|
if interface == INTERFACE
if propname == "ReadMe"
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"), "Property '#{interface}.#{propname}' (on object '#{@path}') is not writable"
elsif propname == "ReadOrWriteMe"
@read_or_write_me = value
self.PropertiesChanged(interface, {propname => value}, [])
elsif propname == "WriteMe"
@read_me = value
self.PropertiesChanged(interface, {"ReadMe" => value}, [])
else
raise DBus.error("org.freedesktop.DBus.Error.InvalidArgs"), "Property '#{interface}.#{propname}' not found on object '#{@path}'"
end
else
raise DBus.error("org.freedesktop.DBus.Error.UnknownInterface"), "Interface '#{interface}' not found on object '#{@path}'"
end
end
dbus_method :GetAll, "in interface:s, out value:a{sv}" do |interface|
if interface == INTERFACE
[ {
"ReadMe" => @read_me,
"ReadOrWriteMe" =>@read_or_write_me,
} ]
else
raise DBus.error("org.freedesktop.DBus.Error.UnknownInterface"), "Interface '#{interface}' not found on object '#{@path}'"
end
end
dbus_signal :PropertiesChanged, "interface:s, changed_properties:a{sv}, invalidated_properties:as"
end
end
class Derived < Test
end
class Test2 < DBus::Object
dbus_interface "org.ruby.Test2" do
dbus_method :hi, "in name:s, out greeting:s" do |name|
"Hi, #{name}!"
end
end
end
bus = DBus::SessionBus.instance
service = bus.request_service("org.ruby.service")
myobj = Test.new("/org/ruby/MyInstance")
service.export(myobj)
derived = Derived.new "/org/ruby/MyDerivedInstance"
service.export derived
test2 = Test2.new "/org/ruby/MyInstance2"
service.export test2
# introspect every other connection, Ticket #34
# (except the one that activates us - it has already emitted
# NOC by the time we run this. Therefore the test for #34 will not work
# by running t2.rb alone, one has to run t1 before it; 'rake' does it)
mr = DBus::MatchRule.new.from_s "type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged'"
bus.add_match(mr) do |msg|
new_unique_name = msg.params[2]
unless new_unique_name.empty?
d "RRRING #{new_unique_name}"
bus.introspect_data(new_unique_name, "/") do
# ignore the result
end
end
end
puts "listening, with ruby-#{RUBY_VERSION}"
main = DBus::Main.new
main << bus
begin
main.run
rescue SystemCallError
# the test driver will kill the bus, that's OK
end
ruby-dbus-0.7.2/test/server_test.rb 0000755 0000041 0000041 00000002411 11741120355 017335 0 ustar www-data www-data #!/usr/bin/env ruby
# Test that a server survives various error cases
require "test/unit"
require "dbus"
class Foo < DBus::Object
dbus_interface "org.ruby.ServerTest" do
dbus_signal :signal_without_arguments
dbus_signal :signal_with_argument, "epsilon:d"
end
dbus_signal :signal_without_interface
rescue DBus::Object::UndefinedInterface => e
# raised by the preceding signal declaration
end
class Bar < DBus::Object
dbus_interface "org.ruby.ServerTest" do
# a valid Ruby symbol but an invalid DBus name; Ticket#38
dbus_signal :signal_with_a_bang!
end
rescue DBus::InvalidMethodName
# raised by the preceding signal declaration
end
class ServerTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.request_service "org.ruby.server-test"
end
def teardown
@bus.proxy.ReleaseName "org.ruby.server-test"
end
def test_unexporting_an_object
obj = Foo.new "/org/ruby/Foo"
@svc.export obj
assert @svc.unexport(obj)
end
def test_unexporting_an_object_not_exported
obj = Foo.new "/org/ruby/Foo"
assert !@svc.unexport(obj)
end
def test_emiting_signals
obj = Foo.new "/org/ruby/Foo"
@svc.export obj
obj.signal_without_arguments
obj.signal_with_argument(-0.1)
end
end
ruby-dbus-0.7.2/test/t1 0000755 0000041 0000041 00000000271 11741120355 014714 0 ustar www-data www-data #! /bin/sh
SEND0="dbus-send --session --dest=org.ruby.service"
CALL="$SEND0 --type=method_call --print-reply"
$CALL /org/ruby/MyInstance org.ruby.AnotherInterface.Reverse string:Hello
ruby-dbus-0.7.2/test/dbus-limited-session.conf 0000644 0000041 0000041 00000001651 11741120355 021357 0 ustar www-data www-data
session
unix:tmpdir=/tmp
tcp:host=localhost,port=0,family=ipv4
50
ruby-dbus-0.7.2/test/test_env 0000755 0000041 0000041 00000000417 11741120355 016221 0 ustar www-data www-data #! /bin/bash
# test_env: set up the environment needed to run tests:
# - set up a private bus
# - run a test server on it
#export DBUS_VERBOSE=1
#export RUBYOPT="-d"
export RUBYOPT="$RUBYOPT -w"
./test_server \
./service_newapi.rb \
-- \
./dbus-launch-simple \
"$@"
ruby-dbus-0.7.2/test/async_test.rb 0000755 0000041 0000041 00000002012 11741120355 017141 0 ustar www-data www-data #!/usr/bin/env ruby
# Test the binding of dbus concepts to ruby concepts
require "test/unit"
require "dbus"
class AsyncTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
@obj = @svc.object "/org/ruby/MyInstance"
@obj.introspect
@obj.default_iface = "org.ruby.SampleInterface"
end
# https://github.com/mvidner/ruby-dbus/issues/13
def test_async_call_to_default_interface
loop = DBus::Main.new
loop << @bus
immediate_answer = @obj.the_answer do |msg, retval|
assert_equal 42, retval
loop.quit
end
assert_nil immediate_answer
# wait for the async reply
loop.run
end
def test_async_call_to_explicit_interface
loop = DBus::Main.new
loop << @bus
ifc = @obj["org.ruby.AnotherInterface"]
immediate_answer = ifc.Reverse("abcd") do |msg, retval|
assert_equal "dcba", retval
loop.quit
end
assert_nil immediate_answer
# wait for the async reply
loop.run
end
end
ruby-dbus-0.7.2/test/property_test.rb 0000755 0000041 0000041 00000003105 11741120355 017714 0 ustar www-data www-data #!/usr/bin/env ruby
require "test/unit"
require "dbus"
class PropertyTest < Test::Unit::TestCase
def setup
session_bus = DBus::ASessionBus.new
svc = session_bus.service("org.ruby.service")
@obj = svc.object("/org/ruby/MyInstance")
@obj.introspect
@iface = @obj["org.ruby.SampleInterface"]
end
def test_property_reading
assert_equal "READ ME", @iface["ReadMe"]
end
def test_property_nonreading
e = assert_raises DBus::Error do
@iface["WriteMe"]
end
assert_match(/not readable/, e.to_s)
end
def test_property_writing
@iface["ReadOrWriteMe"] = "VALUE"
assert_equal "VALUE", @iface["ReadOrWriteMe"]
end
# https://github.com/mvidner/ruby-dbus/pull/19
def test_service_select_timeout
@iface["ReadOrWriteMe"] = "VALUE"
assert_equal "VALUE", @iface["ReadOrWriteMe"]
# wait for the service to become idle
sleep 6
assert_equal "VALUE", @iface["ReadOrWriteMe"], "Property value changed; perhaps the service died and got restarted"
end
def test_property_nonwriting
e = assert_raises DBus::Error do
@iface["ReadMe"] = "WROTE"
end
assert_match(/not writable/, e.to_s)
end
def test_get_all
all = @iface.all_properties
assert_equal ["ReadMe", "ReadOrWriteMe"], all.keys.sort
end
def test_unknown_property_reading
e = assert_raises DBus::Error do
@iface["Spoon"]
end
assert_match(/not found/, e.to_s)
end
def test_unknown_property_writing
e = assert_raises DBus::Error do
@iface["Spoon"] = "FORK"
end
assert_match(/not found/, e.to_s)
end
end
ruby-dbus-0.7.2/test/bus_driver_test.rb 0000755 0000041 0000041 00000000767 11741120355 020207 0 ustar www-data www-data #!/usr/bin/env ruby
# Test the methods of the bus driver
require "test/unit"
require "dbus"
def d(msg)
puts msg if $DEBUG
end
class BusDriverTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
@svc.object("/").introspect
end
def test_exists
assert @svc.exists?, "could not find the service"
nonsvc = @bus.service "org.ruby.nosuchservice"
assert ! nonsvc.exists?, "found a service that should not exist"
end
end
ruby-dbus-0.7.2/test/signal_test.rb 0000755 0000041 0000041 00000002535 11741120355 017313 0 ustar www-data www-data #!/usr/bin/env ruby
# Test the signal handlers
require "test/unit"
require "dbus"
def d(msg)
puts "#{$$} #{msg}" if $DEBUG
end
class SignalHandlerTest < Test::Unit::TestCase
def setup
@session_bus = DBus::ASessionBus.new
svc = @session_bus.service("org.ruby.service")
@obj = svc.object("/org/ruby/MyInstance")
@obj.introspect # necessary
@obj.default_iface = "org.ruby.Loop"
@loop = DBus::Main.new
@loop << @session_bus
end
# testing for commit 017c83 (kkaempf)
def test_overriding_a_handler
counter = 0
@obj.on_signal "LongTaskEnd" do
d "+10"
counter += 10
end
@obj.on_signal "LongTaskEnd" do
d "+1"
counter += 1
end
d "will begin"
@obj.LongTaskBegin 3
quitter = Thread.new do
d "sleep before quit"
# FIXME if we sleep for too long
# the socket will be drained and we deadlock in a select.
# It could be worked around by sending ourselves a Unix signal
# (with a dummy handler) to interrupt the select
sleep 1
d "will quit"
@loop.quit
end
@loop.run
assert_equal 1, counter
end
def test_too_many_rules
100.times do
@obj.on_signal "Whichever" do
puts "not called"
end
end
end
def test_removing_a_nonexistent_rule
@obj.on_signal "DoesNotExist"
end
end
ruby-dbus-0.7.2/test/t5-report-dbus-interface.rb 0000755 0000041 0000041 00000003063 11741120355 021526 0 ustar www-data www-data #!/usr/bin/env ruby
# should report it missing on org.ruby.SampleInterface
# (on object...) instead of on DBus::Proxy::ObjectInterface
require "test/unit"
require "dbus"
class ErrMsgTest < Test::Unit::TestCase
def setup
session_bus = DBus::ASessionBus.new
svc = session_bus.service("org.ruby.service")
@obj = svc.object("/org/ruby/MyInstance")
@obj.introspect # necessary
@obj.default_iface = "org.ruby.SampleInterface"
end
def test_report_dbus_interface
begin
@obj.NoSuchMethod
# a specific exception...
rescue NameError => e
# mentioning DBus and the interface
assert_match(/DBus interface.*#{@obj.default_iface}/, e.to_s)
end
end
def test_report_short_struct
begin
@obj.test_variant ["(ss)", ["too few"] ]
rescue DBus::TypeException => e
assert_match(/1 elements but type info for 2/, e.to_s)
end
end
def test_report_long_struct
begin
@obj.test_variant ["(ss)", ["a", "b", "too many"] ]
rescue DBus::TypeException => e
assert_match(/3 elements but type info for 2/, e.to_s)
end
end
def test_report_nil
nils = [
["(s)", [nil] ], # would get disconnected
["i", nil ],
["a{ss}", {"foo" => nil} ],
]
nils.each do |has_nil|
begin
@obj.test_variant has_nil
rescue DBus::TypeException => e
# TODO want backtrace from the perspective of the caller:
# rescue/reraise in send_sync?
assert_match(/Cannot send nil/, e.to_s)
end
end
end
end
ruby-dbus-0.7.2/test/t2.rb 0000755 0000041 0000041 00000004546 11741120355 015330 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require "test/unit"
require "dbus"
class ValueTest < Test::Unit::TestCase
def setup
session_bus = DBus::ASessionBus.new
svc = session_bus.service("org.ruby.service")
@obj = svc.object("/org/ruby/MyInstance")
@obj.introspect # necessary
@obj.default_iface = "org.ruby.SampleInterface"
end
def test_passing_an_array_through_a_variant
# old explicit typing
@obj.test_variant(["as", ["coucou", "kuku"]])
# automatic typing
@obj.test_variant(["coucou", "kuku"])
@obj.test_variant(["saint", "was that a word or a signature?"])
end
def test_bouncing_a_variant
assert_equal "cuckoo", @obj.bounce_variant("cuckoo")[0]
assert_equal ["coucou", "kuku"], @obj.bounce_variant(["coucou", "kuku"])[0]
assert_equal [], @obj.bounce_variant([])[0]
empty_hash = {}
assert_equal empty_hash, @obj.bounce_variant(empty_hash)[0]
end
# these are ambiguous
def test_pairs_with_a_string
# deprecated
assert_equal "foo", @obj.bounce_variant(["s", "foo"])[0]
assert_equal "foo", @obj.bounce_variant(DBus.variant("s", "foo"))[0]
assert_equal "foo", @obj.bounce_variant([DBus.type("s"), "foo"])[0]
# does not work, because the server side forgets the explicit typing
# assert_equal ["s", "foo"], @obj.bounce_variant(["av", ["s", "foo"]])[0]
# assert_equal ["s", "foo"], @obj.bounce_variant(["as", ["s", "foo"]])[0]
# instead, use this to demonstrate that the variant is passed as expected
assert_equal 4, @obj.variant_size(["s", "four"])[0]
# "av" is the simplest thing that will work,
# shifting the heuristic from a pair to the individual items
assert_equal 2, @obj.variant_size(["av", ["s", "four"]])[0]
end
def test_marshalling_an_array_of_variants
# https://trac.luon.net/ruby-dbus/ticket/30
@obj.default_iface = "org.ruby.Ticket30"
choices = []
choices << ['s', 'Plan A']
choices << ['s', 'Plan B']
# old explicit typing
assert_equal "Do Plan A", @obj.Sybilla(choices)[0]
# automatic typing
assert_equal "Do Plan A", @obj.Sybilla(["Plan A", "Plan B"])[0]
end
def test_service_returning_nonarray
# "warning: default `to_a' will be obsolete"
@obj.the_answer
end
def test_multibyte_string
str = @obj.multibyte_string[0]
assert_equal "あいうえお", str
end
end
ruby-dbus-0.7.2/test/test_server 0000755 0000041 0000041 00000001364 11741120355 016741 0 ustar www-data www-data #! /bin/sh
# A wrapper for DBus tests
# Run a server (directly or by dbus activation) and then the test
# $0 server [args...] -- test [args...]
set -o errexit
while [ "$1" != "--" ]; do
SERVER="$SERVER $1"
shift
done
shift # --
setup_activation () {
SDIR=$XDG_DATA_DIRS/dbus-1/services
mkdir -p $SDIR
# FIXME Name is hardcoded
cat < $SDIR/test.service
[D-BUS Service]
Name=org.ruby.service
Exec=$SERVER
EOF
}
run_server () {
echo -n "Hey, server, get on da bus... "
# start the server
$SERVER & sleep 3
echo "off we go!"
}
export XDG_DATA_DIRS=`mktemp -d dbus.activation.XXXXXX`
RM_FILES="$RM_FILES $XDG_DATA_DIRS"
setup_activation
#run_server
# Clean up at exit.
trap "rm -rf \$RM_FILES" EXIT TERM INT
"$@"
ruby-dbus-0.7.2/test/session_bus_test_manual.rb 0000755 0000041 0000041 00000000640 11741120355 021722 0 ustar www-data www-data #!/usr/bin/env ruby
require "test/unit"
require "dbus"
def d(msg)
puts msg if $DEBUG
end
class SessionBusAddressTest < Test::Unit::TestCase
def setup
# test getting the session bus address even if unset in ENV (Issue#4)
ENV.delete "DBUS_SESSION_BUS_ADDRESS"
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.freedesktop.DBus")
end
def test_connection
assert @svc.exists?
end
end
ruby-dbus-0.7.2/test/server_robustness_test.rb 0000755 0000041 0000041 00000004266 11741120355 021636 0 ustar www-data www-data #!/usr/bin/env ruby
# Test that a server survives various error cases
require "test/unit"
require "dbus"
class ServerRobustnessTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
end
# https://trac.luon.net/ruby-dbus/ticket/31
# the server should not crash
def test_no_such_path_with_introspection
obj = @svc.object "/org/ruby/NotMyInstance"
obj.introspect
assert false, "should have raised"
rescue DBus::Error => e
assert_no_match(/timeout/, e.to_s)
end
def test_no_such_path_without_introspection
obj = @svc.object "/org/ruby/NotMyInstance"
ifc = DBus::ProxyObjectInterface.new(obj, "org.ruby.SampleInterface")
ifc.define_method("the_answer", "out n:i")
ifc.the_answer
assert false, "should have raised"
rescue DBus::Error => e
assert_no_match(/timeout/, e.to_s)
end
def test_a_method_that_raises
obj = @svc.object "/org/ruby/MyInstance"
obj.introspect
obj.default_iface = "org.ruby.SampleInterface"
obj.will_raise
assert false, "should have raised"
rescue DBus::Error => e
assert_no_match(/timeout/, e.to_s)
end
def test_a_method_that_raises_name_error
obj = @svc.object "/org/ruby/MyInstance"
obj.introspect
obj.default_iface = "org.ruby.SampleInterface"
obj.will_raise_name_error
assert false, "should have raised"
rescue DBus::Error => e
assert_no_match(/timeout/, e.to_s)
end
# https://trac.luon.net/ruby-dbus/ticket/31#comment:3
def test_no_such_method_without_introspection
obj = @svc.object "/org/ruby/MyInstance"
ifc = DBus::ProxyObjectInterface.new(obj, "org.ruby.SampleInterface")
ifc.define_method("not_the_answer", "out n:i")
ifc.not_the_answer
assert false, "should have raised"
rescue DBus::Error => e
assert_no_match(/timeout/, e.to_s)
end
def test_no_such_interface_without_introspection
obj = @svc.object "/org/ruby/MyInstance"
ifc = DBus::ProxyObjectInterface.new(obj, "org.ruby.NoSuchInterface")
ifc.define_method("the_answer", "out n:i")
ifc.the_answer
assert false, "should have raised"
rescue DBus::Error => e
assert_no_match(/timeout/, e.to_s)
end
end
ruby-dbus-0.7.2/test/t6-loop.rb 0000755 0000041 0000041 00000004330 11741120355 016272 0 ustar www-data www-data #!/usr/bin/env ruby
# Test the main loop
require "test/unit"
require "dbus"
def d(msg)
puts "#{$$} #{msg}" if $DEBUG
end
class MainLoopTest < Test::Unit::TestCase
def setup
@session_bus = DBus::ASessionBus.new
svc = @session_bus.service("org.ruby.service")
@obj = svc.object("/org/ruby/MyInstance")
@obj.introspect # necessary
@obj.default_iface = "org.ruby.Loop"
@loop = DBus::Main.new
@loop << @session_bus
end
# Hack the library internals so that there is a delay between
# sending a DBus call and listening for its reply, so that
# the bus has a chance to join the server messages and a race is reproducible
def call_lazily
class << @session_bus
alias :wait_for_message_orig :wait_for_message
def wait_for_message_lazy
d "I am so lazy"
sleep 1 # Give the server+bus a chance to join the messages
wait_for_message_orig
end
alias :wait_for_message :wait_for_message_lazy
end
yield
# undo
class << @session_bus
remove_method :wait_for_message
remove_method :wait_for_message_lazy
alias :wait_for_message :wait_for_message_orig
end
end
def test_loop_quit(delay = 1)
@obj.on_signal "LongTaskEnd" do
d "Telling loop to quit"
@loop.quit
end
call_lazily do
# The method will sleep the requested amount of seconds
# (in another thread) before signalling LongTaskEnd
@obj.LongTaskBegin delay
end
# this thread will make the test fail if @loop.run does not return
dynamite = Thread.new do
d "Dynamite burning"
sleep 2
d "Dynamite explodes"
# We need to raise in the main thread.
# Simply raising here means the exception is ignored
# (until dynamite.join which we don't call) or
# (if abort_on_exception is set) it terminates the whole script.
Thread.main.raise RuntimeError, "The main loop did not quit in time"
end
@loop.run
d "Defusing dynamite"
# if we get here, defuse the bomb
dynamite.exit
# remove signal handler
@obj.on_signal "LongTaskEnd"
end
# https://bugzilla.novell.com/show_bug.cgi?id=537401
def test_loop_drained_socket
test_loop_quit 0
end
end
ruby-dbus-0.7.2/test/variant_test.rb 0000755 0000041 0000041 00000003461 11741120355 017501 0 ustar www-data www-data #!/usr/bin/env ruby
# Test marshalling variants according to ruby types
require "test/unit"
require "dbus"
class VariantTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
end
def make_variant(a)
DBus::PacketMarshaller.make_variant(a)
end
def test_make_variant_scalar
# special case: do not fail immediately, marshaller will do that
assert_equal ["b", nil], make_variant(nil)
assert_equal ["b", true], make_variant(true)
# Integers
# no byte
assert_equal ["i", 42], make_variant(42)
# 3_000_000_000 can be u or x.
# less specific test: just run it thru a loopback
assert_equal ["x", 3_000_000_000], make_variant(3_000_000_000)
assert_equal ["x", 5_000_000_000], make_variant(5_000_000_000)
assert_equal ["d", 3.14], make_variant(3.14)
assert_equal ["s", "foo"], make_variant("foo")
assert_equal ["s", "bar"], make_variant(:bar)
# left: strruct, array, dict
# object path: detect exported objects?, signature
# # by Ruby types
# class Foo
# end
# make_variant(Foo.new)
# if we don;t understand a class, the error should be informative -> new exception
end
def test_make_variant_array
ai = [1, 2, 3]
# as = ["one", "two", "three"]
# which?
# assert_equal ["ai", [1, 2, 3]], make_variant(ai)
assert_equal ["av", [["i", 1],
["i", 2],
["i", 3]]], make_variant(ai)
a0 = []
assert_equal ["av", []], make_variant(a0)
end
def test_make_variant_hash
h = {"k1" => "v1", "k2" => "v2"}
assert_equal ["a{sv}", {
"k1" => ["s", "v1"],
"k2" => ["s", "v2"],
}], make_variant(h)
h0 = {}
assert_equal ["a{sv}", {}], make_variant(h0)
end
end
ruby-dbus-0.7.2/test/bus_test.rb 0000755 0000041 0000041 00000000766 11741120355 016633 0 ustar www-data www-data #!/usr/bin/env ruby
# Test the bus class
require "test/unit"
require "dbus"
class BusTest < Test::Unit::TestCase
def setup
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
@svc.object("/").introspect
end
def test_introspection_not_leaking
# peek inside the object to see if a cleanup step worked or not
some_hash = @bus.instance_eval { @method_call_replies || Hash.new }
assert_equal 0, some_hash.size, "there are leftover method handlers"
end
end
ruby-dbus-0.7.2/test/thread_safety_test.rb 0000755 0000041 0000041 00000001473 11741120355 020660 0 ustar www-data www-data #!/usr/bin/env ruby
# Test thread safety
require "test/unit"
require "dbus"
def d(msg)
puts "#{$$} #{msg}" if $DEBUG
end
class ThreadSafetyTest < Test::Unit::TestCase
def test_thread_competition
print "Thread competition: "
jobs = []
5.times do
jobs << Thread.new do
Thread.current.abort_on_exception = true
# use separate connections to avoid races
bus = DBus::ASessionBus.new
svc = bus.service("org.ruby.service")
obj = svc.object("/org/ruby/MyInstance")
obj.introspect
obj.default_iface = "org.ruby.SampleInterface"
10.times do |i|
print "#{i} "
$stdout.flush
assert_equal 42, obj.the_answer[0]
sleep 0.1 * rand
end
end
end
jobs.each do |thread| thread.join end
end
end
ruby-dbus-0.7.2/test/dbus-launch-simple 0000755 0000041 0000041 00000002064 11741120355 020066 0 ustar www-data www-data #! /bin/sh
# A wrapper for DBus tests
# Reimplementing dbus-launch because it is in dbus-1-x11.rpm
# Sets up a private session bus and call the specified program
set -o errexit
# This launches the bus daemon,
# exports DBUS_SESSION_BUS_ADDRESS and sets DBUS_SESSION_BUS_PID
my_dbus_launch () {
# reimplementing dbus-launch because it is in dbus-1-x11.rpm
PF=`mktemp dbus.pid.XXXXXX` || exit
AF=`mktemp dbus.addr.XXXXXX` || exit
RM_FILES="$RM_FILES $PF $AF"
dbus-daemon --config-file=dbus-limited-session.conf --print-address=3 3>$AF --print-pid=4 4>$PF &
# wait for the daemon to print the info
TRIES=0
while [ ! -s $AF -o ! -s $PF ]; do
sleep 0.1
TRIES=`expr $TRIES + 1`
if [ $TRIES -gt 100 ]; then echo "dbus-daemon failed?"; exit 1; fi
done
DBUS_SESSION_BUS_PID=$(cat $PF)
export DBUS_SESSION_BUS_ADDRESS=$(cat $AF)
KILLS="$KILLS $DBUS_SESSION_BUS_PID"
# dbus-monitor &
}
my_dbus_launch
# Clean up at exit.
trap "kill \$KILLS; rm -rf \$RM_FILES" EXIT TERM INT
# run the payload; the return value is passed on
"$@"
ruby-dbus-0.7.2/doc/ 0000755 0000041 0000041 00000000000 11741120355 014230 5 ustar www-data www-data ruby-dbus-0.7.2/doc/tutorial/ 0000755 0000041 0000041 00000000000 11741120355 016073 5 ustar www-data www-data ruby-dbus-0.7.2/doc/tutorial/index.markdown 0000644 0000041 0000041 00000041600 11741120355 020747 0 ustar www-data www-data
Welcome
=======
This is the Ruby D-Bus tutorial. It aims to show you the features of Ruby
D-Bus and as you read through the tutorial also how to use them.
© Arnaud Cornet and Paul van Tilburg; this tutorial is part of
free software; you can redistribute it and/or modify it under the
terms of the [GNU Lesser General Public License,
version 2.1](http://www.gnu.org/licenses/lgpl.html) as published by the
[Free Software Foundation](http://www.fsf.org/).
Introduction
============
This is a tutorial for Ruby D-Bus, a library to access D-Bus facilities of your
system.
What is D-Bus?
--------------
D-Bus is an RPC(Remote Procedure Call) protocol. A common setup can have
multiple D-Bus daemons running that route procedure calls and signals in
the form of messages. Each of these daemons supports a bus. A bus that
is often used by modern desktop environments, and is available per session, is
called the _session bus_. Another bus that can be available, but in a
system-wide manner, is called the _system bus_. It is used for example by
the [Hardware Abstraction Layer](http://hal.freedesktop.org/) daemon. Note
that theoretically the D-Bus RPC protocol can be used without a system or
session bus. I never came across any actual use of this though.
At the desktop level, D-Bus allows some components to interact. Typically
if you are writing an application or a personal script that wants to
interact with your web browser, your music player, or that simply wants to
pop-up a desktop notification, D-Bus comes into play.
At the system level, the Hardware Abstraction Layer is a privileged daemon
that notifies other software of hardware activities. Typically, if you
want to be notified if a CD-ROM has been loaded in, of if you want to
explore hardware, the system daemon comes into play.
The D-Bus RPC system is as we will see _object oriented_.
Buses provide access to _services_ provided in turn by running or ready to
run processes. Let me introduce some D-Bus terminology before we discuss
the API of Ruby D-Bus.
Client
------
A D-Bus client is a process that connects to a D-Bus. They issue method
calls and register to the bus for signals and events.
Service
-------
A connected client can export some of its objects and let other clients
call some of its methods. Such clients typically register a special name
like `org.freedesktop.Notifications`, the service name.
There is slightly different type of service. They are provided by
processes that can be launched by a D-Bus daemon on demand. Once they are
started by D-Bus they register a service name and behave like another
client.
Note that the buses themselves provide the `org.freedesktop.DBus` service,
and provide some features through it.
Object path
-----------
An object path is the D-Bus way to specify an object _instance_ address. A
service can provide different object instances to the outside world, so
that external processes can call methods on each of them. An object path
is an address of an instance in a very similar way that the path is an
address of a file on a file system. For example:
`/org/freedesktop/Notification` is an object path of an object provided by
the `org.freedesktop.Notification` service
**Beware**: service names and object paths can, but do _not_ have to be
related! You'll probably encounter a lot of cases though, where the
object path is a slashed version of the dotted service name.
Interface
---------
Classically in an object model, classes can implement interfaces. That is,
some method definitions grouped in an interface. This is exactly what a
D-Bus interface is as well. In D-Bus interfaces have names. These names must be
specified on method calls.
The `org.freedesktop.Notification` service provides an object instance
called `/org/freedesktop/Notification`. This instance object implements an
interface called `org.freedesktop.Notifications`. It also provides two
special D-Bus specific interfaces: `org.freedesktop.DBus.Introspect` and
`org.freedesktop.DBus.Properties`. Again, object paths, service names,
and interface names can be related but do not have to be.
Basically the `org.freedesktop.DBus.Introspect` has an `Introspect` method,
that returns XML data describing the `/org/freedesktop/Notification` object
interfaces. This is used heavily internally by Ruby D-Bus.
Method
------
A method is, well, a method in the classical meaning. It's a function that
is called in the context of an object instance. Methods have typed
parameters and return typed return values.
Signal
------
Signals are simplified method calls that do not have a return value. They
do have typed parameters though.
Message
-------
Method calls, method returns, signals, errors: all are encoded as D-Bus
messages sent over a bus. They are made of a packet header with source and
destination address, a type (method call, method reply, signal) and the
body containing the parameters (for signals and method calls) or the return
values (for a method return message).
Signature
---------
Because D-Bus is typed and dynamic, each message comes with a signature that
describes the types of the data that is contained within the message. The
signature is a string with an extremely basic language that only describes
a data type. You will need to have some knowledge of what a signature
looks like if you are setting up a service. If you are just programming a
D-Bus client, you can live without knowing about them.
Client Usage
============
This chapter discusses basic client usage
and has the following topics:
Using the library
-----------------
If you want to use the library, you have to make Ruby load it by issuing:
require 'dbus'
That's all! Now we can move on to really using it...
Connecting to a bus
-------------------
On a typical system, two buses are running, the system bus and the session
bus. The system bus can be accessed by:
bus = DBus::SystemBus.instance
Probably you already have guessed how to access the session bus. This
can be done by:
bus = DBus::SessionBus.instance
Performing method calls
-----------------------
Let me continue this example using the session bus. Let's say that I want
to access an object of some client on the session bus. This particular
D-Bus client provides a service called `org.gnome.Rhythmbox`. Let me
access this service:
rb_service = bus.service("org.gnome.Rhythmbox")
In this example I access the `org.gnome.Rhythmbox` service, which is
provided by the application
[Rhythmbox](http://www.gnome.org/projects/rhythmbox/).
OK, I have a service handle now, and I know that it exports the object
"/org/gnome/Rhythmbox/Player". I will trivially access this remote object
using:
rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
Introspection
-------------
Well, that was easy. Let's say that I know that this particular object is
introspectable. In real life most of them are. The `rb_object` object we
have here is just a handle of a remote object, in general they are called
_proxy objects_, because they are the local handle of a remote object. It
would be nice to be able to make it have methods, and that its methods send
a D-Bus call to remotely execute the actual method in another process.
Well, instating these methods for a _introspectable_ object is trivial:
rb_player.introspect
And there you go. Note that not all services or objects can be
introspected, therefore you have to do this manually! Let me remind you
that objects in D-Bus have interfaces and interfaces have methods. Let's
now access these methods:
rb_player_iface = rb_player["org.gnome.Rhythmbox.Player"]
puts rb_player_iface.getPlayingUri
As you can see, when you want to call a method on an instance object, you have
to get the correct interface. It is a bit tedious, so we have the following
shortcut that does the same thing as before:
rb_player.default_iface = "org.gnome.Rhythmbox.Player"
puts rb_player.getPlayingUri
The `default_iface=` call specifies the default interface that should be
used when non existing methods are called directly on a proxy object, and
not on one of its interfaces.
Note that the bus itself has a corresponding introspectable object. You can
access it with `bus.proxy` method. For example, you can retrieve an array of
exported service names of a bus like this:
bus.proxy.ListNames[0]
Properties
----------
Some D-Bus objects provide access to properties. They are accessed by
treating a proxy interface as a hash:
nm_iface = network_manager_object["org.freedesktop.NetworkManager"]
enabled = nm_iface["WirelessEnabled"]
puts "Wireless is " + (enabled ? "enabled":"disabled")
puts "Toggling wireless"
nm_iface["WirelessEnabled"] = ! enabled
Calling a method asynchronously
-------------------------------
D-Bus is _asynchronous_. This means that you do not have to wait for a
reply when you send a message. When you call a remote method that takes a
lot of time to process remotely, you don't want your application to hang,
right? Well the asychronousness exists for this reason. What if you dont'
want to wait for the return value of a method, but still you want to take
some action when you receive it?
There is a classical method to program this event-driven mechanism. You do
some computation, perform some method call, and at the same time you setup
a callback that will be triggered once you receive a reply. Then you run a
main loop that is responsible to call the callbacks properly. Here is how
you do it:
rb_player.getPlayingUri do |resp|
puts "The playing URI is #{resp}"
end
puts "See, I'm not waiting!"
loop = DBus::Main.new
loop << bus
loop.run
This code will print the following:
See, I'm not waiting!
The playing URI is file:///music/papapingoin.mp3
Waiting for a signal
--------------------
Signals are calls from the remote object to your program. As a client, you
set yourself up to receive a signal and handle it with a callback. Then running
the main loop triggers the callback. You can register a callback handler
as allows:
rb_player.on_signal("elapsedChanged") do |u|
puts u
end
More about introspection
------------------------
There are various ways to inspect a remote service. You can simply call
`Introspect()` and read the XML output. However, in this tutorial I assume
that you want to do it using the Ruby D-Bus API.
Notice that you can introspect a service, and not only objects:
rb_service = bus.service("org.gnome.Rhythmbox")
rb_service.introspect
p rb_service.root
This dumps a tree-like structure that represents multiple object paths. In
this particular case the output is:
{gnome => {Rhythmbox => {Player => ..fdbe625de {},Shell => ..fdbe6852e {},PlaylistManager => ..fdbe4e340 {}}>
Read this left to right: the root node is "/", it has one child node "org",
"org" has one child node "gnome", and "gnome" has one child node "Rhythmbox".
Rhythmbox has Tree child nodes "Player", "Shell" and "PlaylistManager".
These three last child nodes have a weird digit that means it has an object
instance. Such object instances are already introspected.
If the prose wasn't clear, maybe the following ASCII art will help you:
/
org
gnome
Rhythmbox
Shell (with object)
Player (with object)
PlaylistManager (with object)
### Walking the object tree
You can have an object on any node, i.e. it is not limited to leaves.
You can access a specific node like this:
rb_player = rb_service.root["org"]["gnome"]["Rhythmbox"]["Player"]
rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
The difference between the two is that for the first one, `rb_service`
needs to have been introspected. Also the obtained `rb_player` is already
introspected whereas the second `rb_player` isn't yet.
Errors
------
D-Bus calls can reply with an error instead of a return value. An error is
translated to a Ruby exception.
begin
network_manager.sleep
rescue DBus::Error => e
puts e unless e.name == "org.freedesktop.NetworkManager.AlreadyAsleepOrAwake"
end
Creating a Service
==================
This chapter deals with the opposite side of the basic client usage, namely
the creation of a D-Bus service.
Registering a service
---------------------
Now that you know how to perform D-Bus calls, and how to wait for and
handle signals, you might want to learn how to publish some object and
interface to provide them to the D-Bus world. Here is how you do that.
As you should already know, D-Bus clients that provide some object to be
called remotely are services. Here is how to allocate a name on a bus:
bus = DBus.session_bus
service = bus.request_service("org.ruby.service")
Now this client is know to the outside world as `org.ruby.service`.
Note that this is a request and it _can_ be denied! When it
is denied, an exception (`DBus::NameRequestError`) is thrown.
Exporting an object
-------------------
Now, let's define a class that we want to export:
class Test < DBus::Object
# Create an interface.
dbus_interface "org.ruby.SampleInterface" do
# Create a hello method in that interface.
dbus_method :hello, "in name:s, in name2:s" do |name, name2|
puts "hello(#{name}, #{name2})"
end
end
end
As you can see, we define a `Test` class in which we define a
`org.ruby.SampleInterface` interface. In this interface, we define a
method. The given code block is the method's implementation. This will be
executed when remote programs performs a D-Bus call. Now the annoying part:
the actual method definition. As you can guess the call
dbus_method :hello, "in name:s, in name2:s" do ...
creates a `hello` method that takes two parameters both of type string.
The _:s_ means "of type string". Let's have a look at some other common
parameter types:
- *u* means unsigned integer
- *i* means integer
- *y* means byte
- *(ui)* means a structure having a unsigned integer and a signed one.
- *a* means array, so that "ai" means array of integers
- *as* means array of string
- *a(is)* means array of structures, each having an integer and a string.
For a full description of the available D-Bus types, please refer to the
[D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures).
Now that the class has been defined, we can instantiate an object
and export it as follows:
exported_obj = Test.new("/org/ruby/MyInstance")
service.export(exported_obj)
This piece of code above instantiates a `Test` object with a D-Bus object
path. This object is reachable from the outside world after
`service.export(exported_obj)` is called.
We also need a loop which will read and process the calls coming over the bus:
loop = DBus::Main.new
loop << bus
loop.run
### Using the exported object
Now, let's consider another program that will access our newly created service:
ruby_service = bus.service("org.ruby.service")
obj = ruby_service.object("/org/ruby/MyInstance")
obj.introspect
obj.default_iface = "org.ruby.SampleInterface"
obj.hello("giligiligiligili", "haaaaaaa")
As you can see, the object we defined earlier is automatically introspectable.
See also "Basic Client Usage".
Emitting a signal
-----------------
Let's add some example method so you can see how to return a value to the
caller and let's also define another example interface that has a signal.
class Test2 < DBus::Object
# Create an interface
dbus_interface "org.ruby.SampleInterface" do
# Create a hello method in the interface:
dbus_method :hello, "in name:s, in name2:s" do |name, name2|
puts "hello(#{name}, #{name2})"
end
# Define a signal in the interface:
dbus_signal :SomethingJustHappened, "toto:s, tutu:u"
end
dbus_interface "org.ruby.AnotherInterface" do
dbus_method :ThatsALongMethodNameIThink, "in name:s, out ret:s" do |name|
["So your name is #{name}"]
end
end
end
Triggering the signal is a easy as calling a method, but then this time on
a local (exported) object and not on a remote/proxy object:
exported_obj.SomethingJustHappened("blah", 1)
Note that the `ThatsALongMethodNameIThink` method is returning a single
value to the caller. Notice that you always have to return an array. If
you want to return multiple values, just have an array with multiple
values.
Replying with an error
----------------------
To reply to a dbus_method with a D-Bus error, raise a `DBus::Error`,
as constructed by the `error` convenience function:
raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{seat} is occupied"
If the error name is not specified, the generic
`org.freedesktop.DBus.Error.Failed` is used.
raise DBus.error, "Seat #{seat} is occupied"
raise DBus.error
ruby-dbus-0.7.2/examples/ 0000755 0000041 0000041 00000000000 11741120355 015301 5 ustar www-data www-data ruby-dbus-0.7.2/examples/no-introspect/ 0000755 0000041 0000041 00000000000 11741120355 020105 5 ustar www-data www-data ruby-dbus-0.7.2/examples/no-introspect/nm-test.rb 0000755 0000041 0000041 00000001077 11741120355 022031 0 ustar www-data www-data #!/usr/bin/env ruby
#
# Trivial network interface lister using NetworkManager.
# NetworkManager does not support introspection, so the api is not that sexy.
require 'dbus'
bus = DBus::SystemBus.instance
nm_service = bus.service("org.freedesktop.NetworkManager")
nm_manager = nm_service.object("/org/freedesktop/NetworkManager")
poi = DBus::ProxyObjectInterface.new(nm_manager, "org.freedesktop.NetworkManager")
begin
poi.define_method("getDevices", "") # NM 0.6
p poi.getDevices
rescue Exception
poi.define_method("GetDevices", "") # NM 0.7
p poi.GetDevices
end
ruby-dbus-0.7.2/examples/no-introspect/tracker-test.rb 0000755 0000041 0000041 00000001161 11741120355 023044 0 ustar www-data www-data #!/usr/bin/env ruby
#
# Trivial network interface lister using NetworkManager.
# NetworkManager does not support introspection, so the api is not that sexy.
require 'dbus'
bus = DBus::SessionBus.instance
tracker_service = bus.service("org.freedesktop.Tracker")
tracker_manager = tracker_service.object("/org/freedesktop/tracker")
poi = DBus::ProxyObjectInterface.new(tracker_manager, "org.freedesktop.Tracker.Files")
poi.define_method("GetMetadataForFilesInFolder", "in live_query_id:i, in uri:s, in fields:as, out values:aas")
p poi.GetMetadataForFilesInFolder(-1, ENV['HOME'] + "/Desktop", ["File:Name", "File:Size"])
ruby-dbus-0.7.2/examples/service/ 0000755 0000041 0000041 00000000000 11741120355 016741 5 ustar www-data www-data ruby-dbus-0.7.2/examples/service/service_newapi.rb 0000755 0000041 0000041 00000002204 11741120355 022272 0 ustar www-data www-data #!/usr/bin/env ruby
require 'dbus'
require 'thread'
Thread.abort_on_exception = true
class Test < DBus::Object
# Create an interface aggregating all upcoming dbus_method defines.
dbus_interface "org.ruby.SampleInterface" do
dbus_method :hello, "in name:s, in name2:s" do |name, name2|
puts "hello(#{name}, #{name2})"
end
dbus_method :test_variant, "in stuff:v" do |variant|
p variant
end
dbus_signal :SomethingJustHappened, "toto:s, tutu:u"
end
dbus_interface "org.ruby.AnotherInterface" do
dbus_method :ThatsALongMethodNameIThink do
puts "ThatsALongMethodNameIThink"
end
dbus_method :Reverse, "in instr:s, out outstr:s" do |instr|
outstr = instr.split(//).reverse.join
puts "got: #{instr}, replying: #{outstr}"
[outstr]
end
end
end
bus = DBus::SessionBus.instance
service = bus.request_service("org.ruby.service")
myobj = Test.new("/org/ruby/MyInstance")
service.export(myobj)
Thread.new do
i = 0
loop do
# Signal emission
myobj.SomethingJustHappened("hey", i += 1)
sleep(0.5)
end
end
puts "listening"
main = DBus::Main.new
main << bus
main.run
ruby-dbus-0.7.2/examples/service/call_service.rb 0000755 0000041 0000041 00000001115 11741120355 021722 0 ustar www-data www-data #!/usr/bin/env ruby
require "dbus"
session_bus = DBus::SessionBus.instance
ruby_srv = session_bus.service("org.ruby.service")
# Get the object from this service
player = ruby_srv.object("/org/ruby/MyInstance")
# Introspect it
puts player.introspect
player.default_iface = "org.ruby.SampleInterface"
player.test_variant(["s", "coucou"])
player.on_signal("SomethingJustHappened") do |u, v|
puts "SomethingJustHappened: #{u} #{v}"
end
player.hello("8=======D", "(_._)")
p player["org.ruby.AnotherInterface"].Reverse("Hello world!")
main = DBus::Main.new
main << session_bus
main.run
ruby-dbus-0.7.2/examples/utils/ 0000755 0000041 0000041 00000000000 11741120355 016441 5 ustar www-data www-data ruby-dbus-0.7.2/examples/utils/notify.rb 0000755 0000041 0000041 00000000572 11741120355 020305 0 ustar www-data www-data #!/usr/bin/env ruby
require 'dbus'
if ARGV.size < 2
puts "Usage:"
puts "notify.rb \"title\" \"body\""
exit
end
d = DBus::SessionBus.instance
o = d.service("org.freedesktop.Notifications").object("/org/freedesktop/Notifications")
o.introspect
i = o["org.freedesktop.Notifications"]
i.Notify('notify.rb', 0, 'info', ARGV[0], ARGV[1], [], {}, 2000) do |ret, param|
end
ruby-dbus-0.7.2/examples/utils/listnames.rb 0000755 0000041 0000041 00000000265 11741120355 020773 0 ustar www-data www-data #!/usr/bin/env ruby
require 'dbus'
d = if ARGV.member?("--system")
DBus::SystemBus.instance
else
DBus::SessionBus.instance
end
d.proxy.ListNames[0].each{ |n| puts "\t#{n}" }
ruby-dbus-0.7.2/examples/simple/ 0000755 0000041 0000041 00000000000 11741120355 016572 5 ustar www-data www-data ruby-dbus-0.7.2/examples/simple/call_introspect.rb 0000755 0000041 0000041 00000001440 11741120355 022306 0 ustar www-data www-data #!/usr/bin/env ruby
require "dbus"
session_bus = DBus::SessionBus.instance
# Get the Rhythmbox service
rhythmbox = session_bus.service("org.gnome.Rhythmbox")
# Get the object from this service
player = rhythmbox.object("/org/gnome/Rhythmbox/Player")
# Introspect it
player.introspect
if player.has_iface? "org.gnome.Rhythmbox.Player"
puts "We have Rhythmbox Player interface"
end
player_with_iface = player["org.gnome.Rhythmbox.Player"]
p player_with_iface.getPlayingUri
# Maybe support default_iface=(iface_str) on an ProxyObject, so
# that this is possible?
player.default_iface = "org.gnome.Rhythmbox.Player"
puts "default_iface test:"
p player.getPlayingUri
player.on_signal("elapsedChanged") do |u|
puts "elapsedChanged: #{u}"
end
main = DBus::Main.new
main << session_bus
main.run
ruby-dbus-0.7.2/examples/simple/properties.rb 0000755 0000041 0000041 00000001007 11741120355 021314 0 ustar www-data www-data #! /usr/bin/env ruby
require 'dbus'
bus = DBus::SystemBus.instance
nm_service = bus["org.freedesktop.NetworkManager"]
network_manager_object = nm_service.object("/org/freedesktop/NetworkManager")
network_manager_object.introspect
nm_iface = network_manager_object["org.freedesktop.NetworkManager"]
# read a property
enabled = nm_iface["WirelessEnabled"]
if enabled
puts "Wireless is enabled"
else
puts "Wireless is disabled"
end
puts "Toggling wireless"
# write a property
nm_iface["WirelessEnabled"] = ! enabled
ruby-dbus-0.7.2/examples/rhythmbox/ 0000755 0000041 0000041 00000000000 11741120355 017325 5 ustar www-data www-data ruby-dbus-0.7.2/examples/rhythmbox/playpause.rb 0000755 0000041 0000041 00000001006 11741120355 021655 0 ustar www-data www-data #!/usr/bin/env ruby
require 'dbus'
bus = DBus::SessionBus.instance
# get a rb object
proxy = bus.introspect("org.gnome.Rhythmbox", "/org/gnome/Rhythmbox/Player")
proxyi = proxy["org.gnome.Rhythmbox.Player"]
# register for signals
mr = DBus::MatchRule.new
mr.type = "signal"
mr.interface = "org.gnome.Rhythmbox.Player"
mr.path = "/org/gnome/Rhythmbox/Player"
bus.add_match(mr) do |msg, first_param|
print msg.member + " "
puts first_param
end
proxyi.playPause(true)
main = DBus::Main.new
main << bus
main.run
ruby-dbus-0.7.2/examples/gdbus/ 0000755 0000041 0000041 00000000000 11741120355 016405 5 ustar www-data www-data ruby-dbus-0.7.2/examples/gdbus/gdbus.glade 0000644 0000041 0000041 00000016444 11741120355 020520 0 ustar www-data www-data
True
GD-Bus
GTK_WINDOW_TOPLEVEL
GTK_WIN_POS_NONE
False
500
400
True
False
True
False
False
GDK_WINDOW_TYPE_HINT_NORMAL
GDK_GRAVITY_NORTH_WEST
True
False
True
True
True
True
True
True
GTK_POS_TOP
False
False
True
True
GTK_POLICY_ALWAYS
GTK_POLICY_ALWAYS
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
False
True
True
Session
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
True
GTK_POLICY_ALWAYS
GTK_POLICY_ALWAYS
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
False
True
True
System
False
False
GTK_JUSTIFY_LEFT
False
False
0.5
0.5
0
0
PANGO_ELLIPSIZE_NONE
-1
False
0
tab
True
False
True
True
GTK_POLICY_ALWAYS
GTK_POLICY_ALWAYS
GTK_SHADOW_IN
GTK_CORNER_TOP_LEFT
True
True
True
False
False
True
False
False
False
True
False
ruby-dbus-0.7.2/examples/gdbus/launch.sh 0000755 0000041 0000041 00000000101 11741120355 020206 0 ustar www-data www-data #!/bin/sh
set -e
# for the lazy typer
ruby -w -I ../../lib gdbus
ruby-dbus-0.7.2/examples/gdbus/gdbus 0000755 0000041 0000041 00000016414 11741120355 017445 0 ustar www-data www-data #!/usr/bin/env ruby
#
# This is a quite complex example using internal lower level API.
# Not a good starting point, but might be usefull if you want to do tricky
# stuff.
# -- Arnaud
require 'dbus'
require 'libglade2'
$enable_system = false
class MethodCallWindow
def initialize(pwindow, intf, meth)
@intf, @meth = intf, meth
@entries = Array.new
@dialog = Gtk::Dialog.new(meth.name, pwindow,
Gtk::Dialog::MODAL | Gtk::Dialog::NO_SEPARATOR,
[Gtk::Stock::OK, Gtk::Dialog::RESPONSE_OK],
[Gtk::Stock::CANCEL, Gtk::Dialog::RESPONSE_CANCEL])
@meth.params.each do |param|
shbox = Gtk::HBox.new(true, 0)
label = Gtk::Label.new("#{param[0]} (#{param[1]})")
input = Gtk::Entry.new
@entries << input
shbox.pack_start(label, true, true, 0)
shbox.pack_start(input, true, true, 0)
@dialog.vbox.pack_start(shbox, true, true, 0)
@dialog.vbox.show_all
end
end
def run
on_ok if @dialog.run == Gtk::Dialog::RESPONSE_OK
@dialog.destroy
end
def on_ok
bus = @intf.object.bus
m = DBus::Message.new(DBus::Message::METHOD_CALL)
m.path = @intf.object.path
m.interface = @intf.name
m.destination = @intf.object.destination
m.member = @meth.name
m.sender = bus.unique_name
@meth.params.each_with_index do |param, idx|
entry = @entries[idx]
data = nil
case param[1]
when "u", "i"
data = entry.text.to_i
when "s"
data = entry.text
when /^a/
begin
data = eval(entry.text)
rescue
puts "Incorrect data: #{data}"
end
end
m.add_param(param[1], data)
end
bus.on_return(m) do |retm|
if retm.is_a?(DBus::Error)
puts "Error: #{retm.inspect}"
else
puts "Method #{m.member} returns: #{retm.params.inspect}"
end
end
bus.send(m.marshall)
end
end
class DBusUI
def initialize
@glade = GladeXML.new("gdbus.glade") { |h| method(h) } # This block is like
# black magic :)
@sessiontreeview = @glade.get_widget("sessiontreeview")
setup_treeview_renderer(@sessiontreeview, 'D-Bus Objects')
@sessiontreeview.selection.signal_connect("changed") do |selection|
on_treeview_selection_changed(selection)
end
@systemtreeview = @glade.get_widget("systemtreeview")
setup_treeview_renderer(@systemtreeview, 'D-Bus Objects')
@systemtreeview.selection.signal_connect("changed") do |selection|
on_treeview_selection_changed(selection)
end
@methsigtreeview = @glade.get_widget("methsigtreeview")
# ierk
setup_methodview_renderer(@methsigtreeview)
@window = @glade.get_widget("window1")
@window.show_all
start_buses
end
def beautify_method(meth)
# Damn, this need to be rewritten :p
s = meth.name + "("
if meth.kind_of?(DBus::Method)
s += (meth.params.collect { |a| "in #{a[0]}:#{a[1]}" } +
meth.rets.collect { |a| "out #{a[0]}:#{a[1]}" }).join(", ")
elsif meth.kind_of?(DBus::Signal)
s += (meth.params.collect { |a| "in #{a[0]}:#{a[1]}" }).join(", ")
end
s += ")"
s
end
def on_treeview_selection_changed(selection)
selected = selection.selected
model = Gtk::ListStore.new(String, String, DBus::Method,
DBus::ProxyObjectInterface)
@methsigtreeview.model = model
if selected
if intf = selected[1]
intf.methods.keys.sort.each do |mi|
m = intf.methods[mi]
subiter = model.append
subiter[0] = beautify_method(m)
subiter[1] = "M"
subiter[2] = m
subiter[3] = intf
end
intf.signals.keys.sort.each do |mi|
m = intf.signals[mi]
subiter = model.append
subiter[0] = beautify_method(m)
subiter[1] = "S"
subiter[2] = m
subiter[3] = intf
end
end
end
end
def on_method_activated(view, path, column)
name = view.model.get_iter(path)[0]
puts "Clicked on: #{name.inspect}"
type = view.model.get_iter(path)[1]
intf = view.model.get_iter(path)[2]
if type == "M"
method = view.model.get_iter(path)[2]
intf = view.model.get_iter(path)[3]
MethodCallWindow.new(@window, intf, method).run
elsif type == "S"
signal = view.model.get_iter(path)[2]
intf = view.model.get_iter(path)[3]
mr = DBus::MatchRule.new.from_signal(intf, signal)
puts "*** Registering matchrule: #{mr.to_s} ***"
intf.object.bus.add_match(mr) do |sig|
puts "Got #{sig.member}(#{sig.params.join(',')})"
end
end
end
def on_sessiontreeview_row_activated(view, path, column)
name = view.model.get_iter(path)[0]
puts "Clicked on: #{name.inspect}"
intf = view.model.get_iter(path)[1]
end
def on_window_delete_event(window, event)
Gtk.main_quit
end
def setup_methodview_renderer(treeview)
renderer = Gtk::CellRendererText.new
col_offset = treeview.insert_column(-1, "T", renderer, 'text' => 1)
col_offset = treeview.insert_column(-1, "Name", renderer, 'text' => 0)
column = treeview.get_column(col_offset - 1)
column.clickable = true
end
def setup_treeview_renderer(treeview, str)
renderer = Gtk::CellRendererText.new
col_offset = treeview.insert_column(-1, str, renderer, 'text' => 0)
column = treeview.get_column(col_offset - 1)
column.clickable = true
end
def process_input(bus)
# THIS is the bad ass loop
# we should return to the glib main loop from time to time. Anyone with a
# proper way to handle it ?
bus.update_buffer
bus.messages.each do |msg|
bus.process(msg)
end
end
def start_buses
# call glibize to get dbus messages from the glib mainloop
DBus::SessionBus.instance.glibize
DBus::SystemBus.instance.glibize if $enable_system
DBus::SessionBus.instance.proxy.ListNames do |msg, names|
fill_treeview(DBus::SessionBus.instance, @sessiontreeview, names)
end
if $enable_system
DBus::SystemBus.instance.proxy.ListNames do |msg, names|
fill_treeview(DBus::SystemBus.instance, @systemtreeview, names)
end
end
end
def walk_node(model, iter, node)
node.each_pair do |key, val|
subiter = model.append(iter)
subiter[0] = key
walk_node(model, subiter, val)
end
unless node.object.nil?
node.object.interfaces.sort.each do |ifname|
subiter = model.append(iter)
subiter[0] = ifname
subiter[1] = node.object[ifname]
end
end
end
def introspect_services(model, bus)
el = @introspect_array.shift
if not el =~ /^:/
iter = model.append(nil)
iter[0] = el
puts "introspecting: #{el}"
begin
service = bus.service(el).introspect
walk_node(model, iter, service.root)
rescue Exception => e
puts "DBus Error:"
puts e.backtrace.join("\n")
end
end
not @introspect_array.empty?
end
def fill_treeview(bus, treeview, array)
model = Gtk::TreeStore.new(String, DBus::ProxyObjectInterface)
treeview.model = model
@introspect_array = array.sort
Gtk::idle_add { introspect_services(model, bus) }
end
def main
Gtk.main
end
end
DBusUI.new.main
ruby-dbus-0.7.2/Rakefile 0000644 0000041 0000041 00000002654 11741120355 015137 0 ustar www-data www-data #! /usr/bin/env ruby
require 'rake'
require 'rake/gempackagetask'
require 'fileutils'
include FileUtils
require 'tmpdir'
require 'rake/rdoctask'
require 'rake/testtask'
desc 'Default: run tests in the proper environment'
task :default => :test
def common_test_task(t)
t.libs << "lib"
t.test_files = FileList['test/*_test.rb', 'test/t[0-9]*.rb']
t.verbose = true
end
Rake::TestTask.new("bare:test") {|t| common_test_task t }
begin
require 'rcov/rcovtask'
Rcov::RcovTask.new("bare:rcov") {|t| common_test_task t }
rescue LoadError
# no rcov, never mind
end
%w(test rcov).each do |tname|
desc "Run bare:#{tname} in the proper environment"
task tname do |t|
cd "test" do
system "./test_env rake bare:#{tname}"
end
end
end
load "ruby-dbus.gemspec"
Rake::GemPackageTask.new(GEMSPEC) do |pkg|
# no other formats needed
end
desc "Build a package from a clone of the local Git repo"
task :package_git do |t|
Dir.mktmpdir do |temp|
sh "git clone . #{temp}"
cd temp do
sh "rake package"
end
cp_r "#{temp}/pkg", "."
end
end
Rake::RDocTask.new do |rd|
rd.rdoc_dir = 'doc/rdoc'
rd.rdoc_files.include("README", "lib/**/*.rb")
# rd.options << "--diagram"
# rd.options << "--all"
end
desc "Render the tutorial in HTML"
task :tutorial => "doc/tutorial/index.html"
file "doc/tutorial/index.html" => "doc/tutorial/index.markdown" do |t|
sh "markdown #{t.prerequisites[0]} > #{t.name}"
end
ruby-dbus-0.7.2/VERSION 0000644 0000041 0000041 00000000006 11741120355 014527 0 ustar www-data www-data 0.7.2
ruby-dbus-0.7.2/metadata.yml 0000644 0000041 0000041 00000004410 11741120355 015765 0 ustar www-data www-data --- !ruby/object:Gem::Specification
name: ruby-dbus
version: !ruby/object:Gem::Version
hash: 7
prerelease:
segments:
- 0
- 7
- 2
version: 0.7.2
platform: ruby
authors:
- Ruby DBus Team
autorequire:
bindir: bin
cert_chain: []
date: 2012-04-05 00:00:00 Z
dependencies: []
description:
email: ruby-dbus-devel@lists.luon.net
executables: []
extensions: []
extra_rdoc_files:
- COPYING
- README
- NEWS
files:
- Rakefile
- VERSION
- doc/tutorial/index.markdown
- examples/gdbus/gdbus
- examples/gdbus/gdbus.glade
- examples/gdbus/launch.sh
- examples/no-introspect/nm-test.rb
- examples/no-introspect/tracker-test.rb
- examples/rhythmbox/playpause.rb
- examples/service/call_service.rb
- examples/service/service_newapi.rb
- examples/simple/call_introspect.rb
- examples/simple/properties.rb
- examples/utils/listnames.rb
- examples/utils/notify.rb
- lib/dbus.rb
- lib/dbus/auth.rb
- lib/dbus/bus.rb
- lib/dbus/error.rb
- lib/dbus/export.rb
- lib/dbus/introspect.rb
- lib/dbus/marshall.rb
- lib/dbus/matchrule.rb
- lib/dbus/message.rb
- lib/dbus/type.rb
- ruby-dbus.gemspec
- test/async_test.rb
- test/binding_test.rb
- test/bus_driver_test.rb
- test/bus_test.rb
- test/dbus-launch-simple
- test/dbus-limited-session.conf
- test/property_test.rb
- test/server_robustness_test.rb
- test/server_test.rb
- test/service_newapi.rb
- test/session_bus_test_manual.rb
- test/signal_test.rb
- test/t1
- test/t2.rb
- test/t3-ticket27.rb
- test/t5-report-dbus-interface.rb
- test/t6-loop.rb
- test/test_env
- test/test_server
- test/thread_safety_test.rb
- test/variant_test.rb
- COPYING
- README
- NEWS
homepage: https://trac.luon.net/ruby-dbus
licenses:
- LGPL v2.1
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 57
segments:
- 1
- 8
- 7
version: 1.8.7
required_rubygems_version: !ruby/object:Gem::Requirement
none: false
requirements:
- - ">="
- !ruby/object:Gem::Version
hash: 3
segments:
- 0
version: "0"
requirements: []
rubyforge_project:
rubygems_version: 1.8.15
signing_key:
specification_version: 3
summary: Ruby module for interaction with D-Bus
test_files: []
ruby-dbus-0.7.2/COPYING 0000644 0000041 0000041 00000063504 11741120355 014526 0 ustar www-data www-data GNU LESSER GENERAL PUBLIC LICENSE
Version 2.1, February 1999
Copyright (C) 1991, 1999 Free Software Foundation, Inc.
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
[This is the first released version of the Lesser GPL. It also counts
as the successor of the GNU Library Public License, version 2, hence
the version number 2.1.]
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
Licenses are intended to guarantee your freedom to share and change
free software--to make sure the software is free for all its users.
This license, the Lesser General Public License, applies to some
specially designated software packages--typically libraries--of the
Free Software Foundation and other authors who decide to use it. You
can use it too, but we suggest you first think carefully about whether
this license or the ordinary General Public License is the better
strategy to use in any particular case, based on the explanations below.
When we speak of free software, we are referring to freedom of use,
not price. Our General Public Licenses are designed to make sure that
you have the freedom to distribute copies of free software (and charge
for this service if you wish); that you receive source code or can get
it if you want it; that you can change the software and use pieces of
it in new free programs; and that you are informed that you can do
these things.
To protect your rights, we need to make restrictions that forbid
distributors to deny you these rights or to ask you to surrender these
rights. These restrictions translate to certain responsibilities for
you if you distribute copies of the library or if you modify it.
For example, if you distribute copies of the library, whether gratis
or for a fee, you must give the recipients all the rights that we gave
you. You must make sure that they, too, receive or can get the source
code. If you link other code with the library, you must provide
complete object files to the recipients, so that they can relink them
with the library after making changes to the library and recompiling
it. And you must show them these terms so they know their rights.
We protect your rights with a two-step method: (1) we copyright the
library, and (2) we offer you this license, which gives you legal
permission to copy, distribute and/or modify the library.
To protect each distributor, we want to make it very clear that
there is no warranty for the free library. Also, if the library is
modified by someone else and passed on, the recipients should know
that what they have is not the original version, so that the original
author's reputation will not be affected by problems that might be
introduced by others.
Finally, software patents pose a constant threat to the existence of
any free program. We wish to make sure that a company cannot
effectively restrict the users of a free program by obtaining a
restrictive license from a patent holder. Therefore, we insist that
any patent license obtained for a version of the library must be
consistent with the full freedom of use specified in this license.
Most GNU software, including some libraries, is covered by the
ordinary GNU General Public License. This license, the GNU Lesser
General Public License, applies to certain designated libraries, and
is quite different from the ordinary General Public License. We use
this license for certain libraries in order to permit linking those
libraries into non-free programs.
When a program is linked with a library, whether statically or using
a shared library, the combination of the two is legally speaking a
combined work, a derivative of the original library. The ordinary
General Public License therefore permits such linking only if the
entire combination fits its criteria of freedom. The Lesser General
Public License permits more lax criteria for linking other code with
the library.
We call this license the "Lesser" General Public License because it
does Less to protect the user's freedom than the ordinary General
Public License. It also provides other free software developers Less
of an advantage over competing non-free programs. These disadvantages
are the reason we use the ordinary General Public License for many
libraries. However, the Lesser license provides advantages in certain
special circumstances.
For example, on rare occasions, there may be a special need to
encourage the widest possible use of a certain library, so that it becomes
a de-facto standard. To achieve this, non-free programs must be
allowed to use the library. A more frequent case is that a free
library does the same job as widely used non-free libraries. In this
case, there is little to gain by limiting the free library to free
software only, so we use the Lesser General Public License.
In other cases, permission to use a particular library in non-free
programs enables a greater number of people to use a large body of
free software. For example, permission to use the GNU C Library in
non-free programs enables many more people to use the whole GNU
operating system, as well as its variant, the GNU/Linux operating
system.
Although the Lesser General Public License is Less protective of the
users' freedom, it does ensure that the user of a program that is
linked with the Library has the freedom and the wherewithal to run
that program using a modified version of the Library.
The precise terms and conditions for copying, distribution and
modification follow. Pay close attention to the difference between a
"work based on the library" and a "work that uses the library". The
former contains code derived from the library, whereas the latter must
be combined with the library in order to run.
GNU LESSER GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any software library or other
program which contains a notice placed by the copyright holder or
other authorized party saying it may be distributed under the terms of
this Lesser General Public License (also called "this License").
Each licensee is addressed as "you".
A "library" means a collection of software functions and/or data
prepared so as to be conveniently linked with application programs
(which use some of those functions and data) to form executables.
The "Library", below, refers to any such software library or work
which has been distributed under these terms. A "work based on the
Library" means either the Library or any derivative work under
copyright law: that is to say, a work containing the Library or a
portion of it, either verbatim or with modifications and/or translated
straightforwardly into another language. (Hereinafter, translation is
included without limitation in the term "modification".)
"Source code" for a work means the preferred form of the work for
making modifications to it. For a library, complete source code means
all the source code for all modules it contains, plus any associated
interface definition files, plus the scripts used to control compilation
and installation of the library.
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running a program using the Library is not restricted, and output from
such a program is covered only if its contents constitute a work based
on the Library (independent of the use of the Library in a tool for
writing it). Whether that is true depends on what the Library does
and what the program that uses the Library does.
1. You may copy and distribute verbatim copies of the Library's
complete source code as you receive it, in any medium, provided that
you conspicuously and appropriately publish on each copy an
appropriate copyright notice and disclaimer of warranty; keep intact
all the notices that refer to this License and to the absence of any
warranty; and distribute a copy of this License along with the
Library.
You may charge a fee for the physical act of transferring a copy,
and you may at your option offer warranty protection in exchange for a
fee.
2. You may modify your copy or copies of the Library or any portion
of it, thus forming a work based on the Library, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) The modified work must itself be a software library.
b) You must cause the files modified to carry prominent notices
stating that you changed the files and the date of any change.
c) You must cause the whole of the work to be licensed at no
charge to all third parties under the terms of this License.
d) If a facility in the modified Library refers to a function or a
table of data to be supplied by an application program that uses
the facility, other than as an argument passed when the facility
is invoked, then you must make a good faith effort to ensure that,
in the event an application does not supply such function or
table, the facility still operates, and performs whatever part of
its purpose remains meaningful.
(For example, a function in a library to compute square roots has
a purpose that is entirely well-defined independent of the
application. Therefore, Subsection 2d requires that any
application-supplied function or table used by this function must
be optional: if the application does not supply it, the square
root function must still compute square roots.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Library,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Library, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote
it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Library.
In addition, mere aggregation of another work not based on the Library
with the Library (or with a work based on the Library) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may opt to apply the terms of the ordinary GNU General Public
License instead of this License to a given copy of the Library. To do
this, you must alter all the notices that refer to this License, so
that they refer to the ordinary GNU General Public License, version 2,
instead of to this License. (If a newer version than version 2 of the
ordinary GNU General Public License has appeared, then you can specify
that version instead if you wish.) Do not make any other change in
these notices.
Once this change is made in a given copy, it is irreversible for
that copy, so the ordinary GNU General Public License applies to all
subsequent copies and derivative works made from that copy.
This option is useful when you wish to copy part of the code of
the Library into a program that is not a library.
4. You may copy and distribute the Library (or a portion or
derivative of it, under Section 2) in object code or executable form
under the terms of Sections 1 and 2 above provided that you accompany
it with the complete corresponding machine-readable source code, which
must be distributed under the terms of Sections 1 and 2 above on a
medium customarily used for software interchange.
If distribution of object code is made by offering access to copy
from a designated place, then offering equivalent access to copy the
source code from the same place satisfies the requirement to
distribute the source code, even though third parties are not
compelled to copy the source along with the object code.
5. A program that contains no derivative of any portion of the
Library, but is designed to work with the Library by being compiled or
linked with it, is called a "work that uses the Library". Such a
work, in isolation, is not a derivative work of the Library, and
therefore falls outside the scope of this License.
However, linking a "work that uses the Library" with the Library
creates an executable that is a derivative of the Library (because it
contains portions of the Library), rather than a "work that uses the
library". The executable is therefore covered by this License.
Section 6 states terms for distribution of such executables.
When a "work that uses the Library" uses material from a header file
that is part of the Library, the object code for the work may be a
derivative work of the Library even though the source code is not.
Whether this is true is especially significant if the work can be
linked without the Library, or if the work is itself a library. The
threshold for this to be true is not precisely defined by law.
If such an object file uses only numerical parameters, data
structure layouts and accessors, and small macros and small inline
functions (ten lines or less in length), then the use of the object
file is unrestricted, regardless of whether it is legally a derivative
work. (Executables containing this object code plus portions of the
Library will still fall under Section 6.)
Otherwise, if the work is a derivative of the Library, you may
distribute the object code for the work under the terms of Section 6.
Any executables containing that work also fall under Section 6,
whether or not they are linked directly with the Library itself.
6. As an exception to the Sections above, you may also combine or
link a "work that uses the Library" with the Library to produce a
work containing portions of the Library, and distribute that work
under terms of your choice, provided that the terms permit
modification of the work for the customer's own use and reverse
engineering for debugging such modifications.
You must give prominent notice with each copy of the work that the
Library is used in it and that the Library and its use are covered by
this License. You must supply a copy of this License. If the work
during execution displays copyright notices, you must include the
copyright notice for the Library among them, as well as a reference
directing the user to the copy of this License. Also, you must do one
of these things:
a) Accompany the work with the complete corresponding
machine-readable source code for the Library including whatever
changes were used in the work (which must be distributed under
Sections 1 and 2 above); and, if the work is an executable linked
with the Library, with the complete machine-readable "work that
uses the Library", as object code and/or source code, so that the
user can modify the Library and then relink to produce a modified
executable containing the modified Library. (It is understood
that the user who changes the contents of definitions files in the
Library will not necessarily be able to recompile the application
to use the modified definitions.)
b) Use a suitable shared library mechanism for linking with the
Library. A suitable mechanism is one that (1) uses at run time a
copy of the library already present on the user's computer system,
rather than copying library functions into the executable, and (2)
will operate properly with a modified version of the library, if
the user installs one, as long as the modified version is
interface-compatible with the version that the work was made with.
c) Accompany the work with a written offer, valid for at
least three years, to give the same user the materials
specified in Subsection 6a, above, for a charge no more
than the cost of performing this distribution.
d) If distribution of the work is made by offering access to copy
from a designated place, offer equivalent access to copy the above
specified materials from the same place.
e) Verify that the user has already received a copy of these
materials or that you have already sent this user a copy.
For an executable, the required form of the "work that uses the
Library" must include any data and utility programs needed for
reproducing the executable from it. However, as a special exception,
the materials to be distributed need not include anything that is
normally distributed (in either source or binary form) with the major
components (compiler, kernel, and so on) of the operating system on
which the executable runs, unless that component itself accompanies
the executable.
It may happen that this requirement contradicts the license
restrictions of other proprietary libraries that do not normally
accompany the operating system. Such a contradiction means you cannot
use both them and the Library together in an executable that you
distribute.
7. You may place library facilities that are a work based on the
Library side-by-side in a single library together with other library
facilities not covered by this License, and distribute such a combined
library, provided that the separate distribution of the work based on
the Library and of the other library facilities is otherwise
permitted, and provided that you do these two things:
a) Accompany the combined library with a copy of the same work
based on the Library, uncombined with any other library
facilities. This must be distributed under the terms of the
Sections above.
b) Give prominent notice with the combined library of the fact
that part of it is a work based on the Library, and explaining
where to find the accompanying uncombined form of the same work.
8. You may not copy, modify, sublicense, link with, or distribute
the Library except as expressly provided under this License. Any
attempt otherwise to copy, modify, sublicense, link with, or
distribute the Library is void, and will automatically terminate your
rights under this License. However, parties who have received copies,
or rights, from you under this License will not have their licenses
terminated so long as such parties remain in full compliance.
9. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Library or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Library (or any work based on the
Library), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Library or works based on it.
10. Each time you redistribute the Library (or any work based on the
Library), the recipient automatically receives a license from the
original licensor to copy, distribute, link with or modify the Library
subject to these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties with
this License.
11. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Library at all. For example, if a patent
license would not permit royalty-free redistribution of the Library by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Library.
If any portion of this section is held invalid or unenforceable under any
particular circumstance, the balance of the section is intended to apply,
and the section as a whole is intended to apply in other circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
12. If the distribution and/or use of the Library is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Library under this License may add
an explicit geographical distribution limitation excluding those countries,
so that distribution is permitted only in or among countries not thus
excluded. In such case, this License incorporates the limitation as if
written in the body of this License.
13. The Free Software Foundation may publish revised and/or new
versions of the Lesser General Public License from time to time.
Such new versions will be similar in spirit to the present version,
but may differ in detail to address new problems or concerns.
Each version is given a distinguishing version number. If the Library
specifies a version number of this License which applies to it and
"any later version", you have the option of following the terms and
conditions either of that version or of any later version published by
the Free Software Foundation. If the Library does not specify a
license version number, you may choose any version ever published by
the Free Software Foundation.
14. If you wish to incorporate parts of the Library into other free
programs whose distribution conditions are incompatible with these,
write to the author to ask for permission. For software which is
copyrighted by the Free Software Foundation, write to the Free
Software Foundation; we sometimes make exceptions for this. Our
decision will be guided by the two goals of preserving the free status
of all derivatives of our free software and of promoting the sharing
and reuse of software generally.
NO WARRANTY
15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Libraries
If you develop a new library, and you want it to be of the greatest
possible use to the public, we recommend making it free software that
everyone can redistribute and change. You can do so by permitting
redistribution under these terms (or, alternatively, under the terms of the
ordinary General Public License).
To apply these terms, attach the following notices to the library. It is
safest to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C)
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the library, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
library `Frob' (a library for tweaking knobs) written by James Random Hacker.
, 1 April 1990
Ty Coon, President of Vice
That's all there is to it!
ruby-dbus-0.7.2/ruby-dbus.gemspec 0000644 0000041 0000041 00000001240 11741120355 016741 0 ustar www-data www-data # -*- ruby -*-
require "rubygems"
require "rake"
GEMSPEC = Gem::Specification.new do |s|
s.name = "ruby-dbus"
# s.rubyforge_project = nil
s.summary = "Ruby module for interaction with D-Bus"
# s.description = FIXME
s.version = File.read("VERSION").strip
s.license = "LGPL v2.1"
s.author = "Ruby DBus Team"
s.email = "ruby-dbus-devel@lists.luon.net"
s.homepage = "https://trac.luon.net/ruby-dbus"
s.files = FileList["{doc/tutorial,examples,lib,test}/**/*", "Rakefile", "ruby-dbus.gemspec", "VERSION"].to_a.sort
s.require_path = "lib"
s.has_rdoc = true
s.extra_rdoc_files = ["COPYING", "README", "NEWS"]
s.required_ruby_version = ">= 1.8.7"
end
ruby-dbus-0.7.2/lib/ 0000755 0000041 0000041 00000000000 11741120355 014231 5 ustar www-data www-data ruby-dbus-0.7.2/lib/dbus/ 0000755 0000041 0000041 00000000000 11741120355 015166 5 ustar www-data www-data ruby-dbus-0.7.2/lib/dbus/export.rb 0000644 0000041 0000041 00000011417 11741120355 017040 0 ustar www-data www-data # dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
require 'thread'
module DBus
# Exported object type
# = Exportable D-Bus object class
#
# Objects that are going to be exported by a D-Bus service
# should inherit from this class. At the client side, use ProxyObject.
class Object
# The path of the object.
attr_reader :path
# The interfaces that the object supports.
# intfs: Hash: String => Interface
# @@intfs_hash simulates a class attribute by being a hash Class => intfs
@@intfs_hash = {DBus::Object => nil}
# The service that the object is exported by.
attr_writer :service
@@cur_intf = nil # Interface
@@intfs_mutex = Mutex.new
# Create a new object with a given _path_.
# Use Service#export to export it.
def initialize(path)
@path = path
@service = nil
end
def self.intfs
if self.equal? DBus::Object
@@intfs_hash[DBus::Object]
else
@@intfs_hash[self] || self.superclass.intfs
end
end
def self.intfs= param
@@intfs_hash[self] = param
end
def intfs
self.class.intfs
end
def intfs= param
self.class.intfs = param
end
# State that the object implements the given _intf_.
def implements(intf)
# use a setter
self.intfs = (self.intfs || {}).merge({intf.name => intf})
end
# Dispatch a message _msg_ to call exported methods
def dispatch(msg)
case msg.message_type
when Message::METHOD_CALL
reply = nil
begin
if not self.intfs[msg.interface]
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
"Interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
end
meth = self.intfs[msg.interface].methods[msg.member.to_sym]
if not meth
raise DBus.error("org.freedesktop.DBus.Error.UnknownMethod"),
"Method \"#{msg.member}\" on interface \"#{msg.interface}\" of object \"#{msg.path}\" doesn't exist"
end
methname = Object.make_method_name(msg.interface, msg.member)
retdata = method(methname).call(*msg.params)
retdata = [*retdata]
reply = Message.method_return(msg)
meth.rets.zip(retdata).each do |rsig, rdata|
reply.add_param(rsig.type, rdata)
end
rescue => ex
reply = ErrorMessage.from_exception(ex).reply_to(msg)
end
@service.bus.send(reply.marshall)
end
end
# Select (and create) the interface that the following defined methods
# belong to.
def self.dbus_interface(s)
@@intfs_mutex.synchronize do
unless @@cur_intf = (self.intfs && self.intfs[s])
@@cur_intf = Interface.new(s)
self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
end
yield
@@cur_intf = nil
end
end
# Dummy undefined interface class.
class UndefinedInterface < ScriptError
def initialize(sym)
super "No interface specified for #{sym}"
end
end
# Defines an exportable method on the object with the given name _sym_,
# _prototype_ and the code in a block.
def self.dbus_method(sym, protoype = "", &block)
raise UndefinedInterface, sym if @@cur_intf.nil?
@@cur_intf.define(Method.new(sym.to_s).from_prototype(protoype))
define_method(Object.make_method_name(@@cur_intf.name, sym.to_s), &block)
end
# Emits a signal from the object with the given _interface_, signal
# _sig_ and arguments _args_.
def emit(intf, sig, *args)
@service.bus.emit(@service, self, intf, sig, *args)
end
# Defines a signal for the object with a given name _sym_ and _prototype_.
def self.dbus_signal(sym, protoype = "")
raise UndefinedInterface, sym if @@cur_intf.nil?
cur_intf = @@cur_intf
signal = Signal.new(sym.to_s).from_prototype(protoype)
cur_intf.define(Signal.new(sym.to_s).from_prototype(protoype))
define_method(sym.to_s) do |*args|
emit(cur_intf, signal, *args)
end
end
####################################################################
private
# Helper method that returns a method name generated from the interface
# name _intfname_ and method name _methname_.
def self.make_method_name(intfname, methname)
"#{intfname}%%#{methname}"
end
end # class Object
end # module DBus
ruby-dbus-0.7.2/lib/dbus/marshall.rb 0000644 0000041 0000041 00000032071 11741120355 017321 0 ustar www-data www-data # dbus.rb - Module containing the low-level D-Bus implementation
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
require 'socket'
# = D-Bus main module
#
# Module containing all the D-Bus modules and classes.
module DBus
# Exception raised when an invalid packet is encountered.
class InvalidPacketException < Exception
end
# = D-Bus packet unmarshaller class
#
# Class that handles the conversion (unmarshalling) of payload data
# to Array.
class PacketUnmarshaller
# Index pointer that points to the byte in the data that is
# currently being processed.
#
# Used to kown what part of the buffer has been consumed by unmarshalling.
# FIXME: Maybe should be accessed with a "consumed_size" method.
attr_reader :idx
# Create a new unmarshaller for the given data _buffer_ and _endianness_.
def initialize(buffer, endianness)
@buffy, @endianness = buffer.dup, endianness
if @endianness == BIG_END
@uint32 = "N"
@uint16 = "n"
@double = "G"
elsif @endianness == LIL_END
@uint32 = "V"
@uint16 = "v"
@double = "E"
else
raise InvalidPacketException, "Incorrect endianness #{@endianness}"
end
@idx = 0
end
# Unmarshall the buffer for a given _signature_ and length _len_.
# Return an array of unmarshalled objects
def unmarshall(signature, len = nil)
if len != nil
if @buffy.bytesize < @idx + len
raise IncompleteBufferException
end
end
sigtree = Type::Parser.new(signature).parse
ret = Array.new
sigtree.each do |elem|
ret << do_parse(elem)
end
ret
end
# Align the pointer index on a byte index of _a_, where a
# must be 1, 2, 4 or 8.
def align(a)
case a
when 1
when 2, 4, 8
bits = a - 1
@idx = @idx + bits & ~bits
raise IncompleteBufferException if @idx > @buffy.bytesize
else
raise "Unsupported alignment #{a}"
end
end
###############################################################
# FIXME: does anyone except the object itself call the above methods?
# Yes : Message marshalling code needs to align "body" to 8 byte boundary
private
# Retrieve the next _nbytes_ number of bytes from the buffer.
def get(nbytes)
raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
ret = @buffy.slice(@idx, nbytes)
@idx += nbytes
ret
end
# Retrieve the series of bytes until the next NULL (\0) byte.
def get_nul_terminated
raise IncompleteBufferException if not @buffy[@idx..-1] =~ /^([^\0]*)\0/
str = $1
raise IncompleteBufferException if @idx + str.bytesize + 1 > @buffy.bytesize
@idx += str.bytesize + 1
str
end
# Get the string length and string itself from the buffer.
# Return the string.
def get_string
align(4)
str_sz = get(4).unpack(@uint32)[0]
ret = @buffy.slice(@idx, str_sz)
raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
@idx += str_sz
if @buffy[@idx].ord != 0
raise InvalidPacketException, "String is not nul-terminated"
end
@idx += 1
# no exception, see check above
ret
end
# Get the signature length and signature itself from the buffer.
# Return the signature.
def get_signature
str_sz = get(1).unpack('C')[0]
ret = @buffy.slice(@idx, str_sz)
raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize
@idx += str_sz
if @buffy[@idx].ord != 0
raise InvalidPacketException, "Type is not nul-terminated"
end
@idx += 1
# no exception, see check above
ret
end
# Based on the _signature_ type, retrieve a packet from the buffer
# and return it.
def do_parse(signature)
packet = nil
case signature.sigtype
when Type::BYTE
packet = get(1).unpack("C")[0]
when Type::UINT16
align(2)
packet = get(2).unpack(@uint16)[0]
when Type::INT16
align(4)
packet = get(4).unpack(@uint16)[0]
if (packet & 0x8000) != 0
packet -= 0x10000
end
when Type::UINT32
align(4)
packet = get(4).unpack(@uint32)[0]
when Type::INT32
align(4)
packet = get(4).unpack(@uint32)[0]
if (packet & 0x80000000) != 0
packet -= 0x100000000
end
when Type::UINT64
align(8)
packet_l = get(4).unpack(@uint32)[0]
packet_h = get(4).unpack(@uint32)[0]
if @endianness == LIL_END
packet = packet_l + packet_h * 2**32
else
packet = packet_l * 2**32 + packet_h
end
when Type::INT64
align(8)
packet_l = get(4).unpack(@uint32)[0]
packet_h = get(4).unpack(@uint32)[0]
if @endianness == LIL_END
packet = packet_l + packet_h * 2**32
else
packet = packet_l * 2**32 + packet_h
end
if (packet & 0x8000000000000000) != 0
packet -= 0x10000000000000000
end
when Type::DOUBLE
align(8)
packet = get(8).unpack(@double)[0]
when Type::BOOLEAN
align(4)
v = get(4).unpack(@uint32)[0]
raise InvalidPacketException if not [0, 1].member?(v)
packet = (v == 1)
when Type::ARRAY
align(4)
# checks please
array_sz = get(4).unpack(@uint32)[0]
raise InvalidPacketException if array_sz > 67108864
align(signature.child.alignment)
raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize
packet = Array.new
start_idx = @idx
while @idx - start_idx < array_sz
packet << do_parse(signature.child)
end
if signature.child.sigtype == Type::DICT_ENTRY then
packet = packet.inject(Hash.new) do |hash, pair|
hash[pair[0]] = pair[1]
hash
end
end
when Type::STRUCT
align(8)
packet = Array.new
signature.members.each do |elem|
packet << do_parse(elem)
end
when Type::VARIANT
string = get_signature
# error checking please
sig = Type::Parser.new(string).parse[0]
align(sig.alignment)
packet = do_parse(sig)
when Type::OBJECT_PATH
packet = get_string
when Type::STRING
packet = get_string
packet.force_encoding('UTF-8') if RUBY_VERSION >= '1.9'
when Type::SIGNATURE
packet = get_signature
when Type::DICT_ENTRY
align(8)
key = do_parse(signature.members[0])
value = do_parse(signature.members[1])
packet = [key, value]
else
raise NotImplementedError,
"sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
end
packet
end # def do_parse
end # class PacketUnmarshaller
# D-Bus packet marshaller class
#
# Class that handles the conversion (marshalling) of Ruby objects to
# (binary) payload data.
class PacketMarshaller
# The current or result packet.
# FIXME: allow access only when marshalling is finished
attr_reader :packet
# Create a new marshaller, setting the current packet to the
# empty packet.
def initialize(offset = 0)
@packet = ""
@offset = offset # for correct alignment of nested marshallers
end
# Round _n_ up to the specified power of two, _a_
def num_align(n, a)
case a
when 1, 2, 4, 8
bits = a - 1
n + bits & ~bits
else
raise "Unsupported alignment"
end
end
# Align the buffer with NULL (\0) bytes on a byte length of _a_.
def align(a)
@packet = @packet.ljust(num_align(@offset + @packet.bytesize, a) - @offset, 0.chr)
end
# Append the the string _str_ itself to the packet.
def append_string(str)
align(4)
@packet += [str.bytesize].pack("L") + [str].pack("Z*")
end
# Append the the signature _signature_ itself to the packet.
def append_signature(str)
@packet += str.bytesize.chr + str + "\0"
end
# Append the array type _type_ to the packet and allow for appending
# the child elements.
def array(type)
# Thanks to Peter Rullmann for this line
align(4)
sizeidx = @packet.bytesize
@packet += "ABCD"
align(type.alignment)
contentidx = @packet.bytesize
yield
sz = @packet.bytesize - contentidx
raise InvalidPacketException if sz > 67108864
@packet[sizeidx...sizeidx + 4] = [sz].pack("L")
end
# Align and allow for appending struct fields.
def struct
align(8)
yield
end
# Append a string of bytes without type.
def append_simple_string(s)
@packet += s + "\0"
end
# Append a value _val_ to the packet based on its _type_.
#
# Host native endianness is used, declared in Message#marshall
def append(type, val)
raise TypeException, "Cannot send nil" if val.nil?
type = type.chr if type.kind_of?(Fixnum)
type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
case type.sigtype
when Type::BYTE
@packet += val.chr
when Type::UINT32
align(4)
@packet += [val].pack("L")
when Type::UINT64
align(8)
@packet += [val].pack("Q")
when Type::INT64
align(8)
@packet += [val].pack("q")
when Type::INT32
align(4)
@packet += [val].pack("l")
when Type::UINT16
align(2)
@packet += [val].pack("S")
when Type::INT16
align(2)
@packet += [val].pack("s")
when Type::DOUBLE
align(8)
@packet += [val].pack("d")
when Type::BOOLEAN
align(4)
if val
@packet += [1].pack("L")
else
@packet += [0].pack("L")
end
when Type::OBJECT_PATH
append_string(val)
when Type::STRING
append_string(val)
when Type::SIGNATURE
append_signature(val)
when Type::VARIANT
vartype = nil
if val.is_a?(Array) and val.size == 2
if val[0].is_a?(DBus::Type::Type)
vartype, vardata = val
elsif val[0].is_a?(String)
begin
parsed = Type::Parser.new(val[0]).parse
vartype = parsed[0] if parsed.size == 1
vardata = val[1]
rescue Type::SignatureException
# no assignment
end
end
end
if vartype.nil?
vartype, vardata = PacketMarshaller.make_variant(val)
vartype = Type::Parser.new(vartype).parse[0]
end
append_signature(vartype.to_s)
align(vartype.alignment)
sub = PacketMarshaller.new(@offset + @packet.bytesize)
sub.append(vartype, vardata)
@packet += sub.packet
when Type::ARRAY
if val.kind_of?(Hash)
raise TypeException, "Expected an Array but got a Hash" if type.child.sigtype != Type::DICT_ENTRY
# Damn ruby rocks here
val = val.to_a
end
if not val.kind_of?(Array)
raise TypeException, "Expected an Array but got a #{val.class}"
end
array(type.child) do
val.each do |elem|
append(type.child, elem)
end
end
when Type::STRUCT, Type::DICT_ENTRY
# TODO use duck typing, val.respond_to?
raise TypeException, "Struct/DE expects an Array" if not val.kind_of?(Array)
if type.sigtype == Type::DICT_ENTRY and val.size != 2
raise TypeException, "Dict entry expects a pair"
end
if type.members.size != val.size
raise TypeException, "Struct/DE has #{val.size} elements but type info for #{type.members.size}"
end
struct do
type.members.zip(val).each do |t, v|
append(t, v)
end
end
else
raise NotImplementedError,
"sigtype: #{type.sigtype} (#{type.sigtype.chr})"
end
end # def append
# Make a [signature, value] pair for a variant
def self.make_variant(value)
# TODO: mix in _make_variant to String, Integer...
if value == true
["b", true]
elsif value == false
["b", false]
elsif value.nil?
["b", nil]
elsif value.is_a? Float
["d", value]
elsif value.is_a? Symbol
["s", value.to_s]
elsif value.is_a? Array
["av", value.map {|i| make_variant(i) }]
elsif value.is_a? Hash
h = {}
value.each_key {|k| h[k] = make_variant(value[k]) }
["a{sv}", h]
elsif value.respond_to? :to_str
["s", value.to_str]
elsif value.respond_to? :to_int
i = value.to_int
if -2_147_483_648 <= i && i < 2_147_483_648
["i", i]
else
["x", i]
end
end
end
end # class PacketMarshaller
end # module DBus
ruby-dbus-0.7.2/lib/dbus/message.rb 0000644 0000041 0000041 00000022445 11741120355 017146 0 ustar www-data www-data # dbus.rb - Module containing the low-level D-Bus implementation
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
# = D-Bus main module
#
# Module containing all the D-Bus modules and classes.
module DBus
# = InvalidDestinationName class
# Thrown when you try to send a message to /org/freedesktop/DBus/Local, that
# is reserved.
class InvalidDestinationName < Exception
end
# = D-Bus message class
#
# Class that holds any type of message that travels over the bus.
class Message
# The serial number of the message.
@@serial = 1
# Mutex that protects updates on the serial number.
@@serial_mutex = Mutex.new
# Type of a message (by specification).
MESSAGE_SIGNATURE = "yyyyuua(yv)"
# FIXME: following message type constants should be under Message::Type IMO
# well, yeah sure
#
# Invalid message type.
INVALID = 0
# Method call message type.
METHOD_CALL = 1
# Method call return value message type.
METHOD_RETURN = 2
# Error message type.
ERROR = 3
# Signal message type.
SIGNAL = 4
# Message flag signyfing that no reply is expected.
NO_REPLY_EXPECTED = 0x1
# Message flag signifying that no automatic start is required/must be
# performed.
NO_AUTO_START = 0x2
# The type of the message.
attr_reader :message_type
# The path of the object instance the message must be sent to/is sent from.
attr_accessor :path
# The interface of the object that must be used/was used.
attr_accessor :interface
# The interface member (method/signal name) of the object that must be
# used/was used.
attr_accessor :member
# The name of the error (in case of an error message type).
attr_accessor :error_name
# The destination connection of the object that must be used/was used.
attr_accessor :destination
# The sender of the message.
attr_accessor :sender
# The signature of the message contents.
attr_accessor :signature
# The serial number of the message this message is a reply for.
attr_accessor :reply_serial
# The protocol.
attr_reader :protocol
# The serial of the message.
attr_reader :serial
# The parameters of the message.
attr_reader :params
# Create a message with message type _mtype_ with default values and a
# unique serial number.
def initialize(mtype = INVALID)
@message_type = mtype
@flags = 0
@protocol = 1
@body_length = 0
@signature = String.new
@@serial_mutex.synchronize do
@serial = @@serial
@@serial += 1
end
@params = Array.new
@destination = nil
@interface = nil
@error_name = nil
@member = nil
@path = nil
@reply_serial = nil
if mtype == METHOD_RETURN
@flags = NO_REPLY_EXPECTED
end
end
# Create a regular reply to a message _m_.
def self.method_return(m)
MethodReturnMessage.new.reply_to(m)
end
# Create an error reply to a message _m_.
def self.error(m, error_name, description=nil)
ErrorMessage.new(error_name, description).reply_to(m)
end
# Mark this message as a reply to a another message _m_, taking
# the serial number of _m_ as reply serial and the sender of _m_ as
# destination.
def reply_to(m)
@reply_serial = m.serial
@destination = m.sender
self
end
# Add a parameter _val_ of type _type_ to the message.
def add_param(type, val)
type = type.chr if type.kind_of?(Fixnum)
@signature += type.to_s
@params << [type, val]
end
# FIXME: what are these? a message element constant enumeration?
# See method below, in a message, you have and array of optional parameters
# that come with an index, to determine their meaning. The values are in
# spec, more a definition than an enumeration.
PATH = 1
INTERFACE = 2
MEMBER = 3
ERROR_NAME = 4
REPLY_SERIAL = 5
DESTINATION = 6
SENDER = 7
SIGNATURE = 8
# Marshall the message with its current set parameters and return
# it in a packet form.
def marshall
if @path == "/org/freedesktop/DBus/Local"
raise InvalidDestinationName
end
params = PacketMarshaller.new
@params.each do |param|
params.append(param[0], param[1])
end
@body_length = params.packet.bytesize
marshaller = PacketMarshaller.new
marshaller.append(Type::BYTE, HOST_END)
marshaller.append(Type::BYTE, @message_type)
marshaller.append(Type::BYTE, @flags)
marshaller.append(Type::BYTE, @protocol)
marshaller.append(Type::UINT32, @body_length)
marshaller.append(Type::UINT32, @serial)
marshaller.array(Type::Parser.new("y").parse[0]) do
if @path
marshaller.struct do
marshaller.append(Type::BYTE, PATH)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("o")
marshaller.append(Type::OBJECT_PATH, @path)
end
end
if @interface
marshaller.struct do
marshaller.append(Type::BYTE, INTERFACE)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("s")
marshaller.append(Type::STRING, @interface)
end
end
if @member
marshaller.struct do
marshaller.append(Type::BYTE, MEMBER)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("s")
marshaller.append(Type::STRING, @member)
end
end
if @error_name
marshaller.struct do
marshaller.append(Type::BYTE, ERROR_NAME)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("s")
marshaller.append(Type::STRING, @error_name)
end
end
if @reply_serial
marshaller.struct do
marshaller.append(Type::BYTE, REPLY_SERIAL)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("u")
marshaller.append(Type::UINT32, @reply_serial)
end
end
if @destination
marshaller.struct do
marshaller.append(Type::BYTE, DESTINATION)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("s")
marshaller.append(Type::STRING, @destination)
end
end
if @signature != ""
marshaller.struct do
marshaller.append(Type::BYTE, SIGNATURE)
marshaller.append(Type::BYTE, 1)
marshaller.append_simple_string("g")
marshaller.append(Type::SIGNATURE, @signature)
end
end
end
marshaller.align(8)
@params.each do |param|
marshaller.append(param[0], param[1])
end
marshaller.packet
end
# Unmarshall a packet contained in the buffer _buf_ and set the
# parameters of the message object according the data found in the
# buffer.
# Return the detected message and the index pointer of the buffer where
# the message data ended.
def unmarshall_buffer(buf)
buf = buf.dup
if buf[0] == ?l
endianness = LIL_END
else
endianness = BIG_END
end
pu = PacketUnmarshaller.new(buf, endianness)
mdata = pu.unmarshall(MESSAGE_SIGNATURE)
dummy, @message_type, @flags, @protocol, @body_length, @serial,
headers = mdata
headers.each do |struct|
case struct[0]
when PATH
@path = struct[1]
when INTERFACE
@interface = struct[1]
when MEMBER
@member = struct[1]
when ERROR_NAME
@error_name = struct[1]
when REPLY_SERIAL
@reply_serial = struct[1]
when DESTINATION
@destination = struct[1]
when SENDER
@sender = struct[1]
when SIGNATURE
@signature = struct[1]
end
end
pu.align(8)
if @body_length > 0 and @signature
@params = pu.unmarshall(@signature, @body_length)
end
[self, pu.idx]
end # def unmarshall_buf
# Unmarshall the data of a message found in the buffer _buf_ using
# Message#unmarshall_buf.
# Return the message.
def unmarshall(buf)
ret, size = unmarshall_buffer(buf)
ret
end
end # class Message
class MethodReturnMessage < Message
def initialize
super(METHOD_RETURN)
end
end
class ErrorMessage < Message
def initialize(error_name, description=nil)
super(ERROR)
@error_name = error_name
unless description.nil?
add_param(Type::STRING, description)
end
end
def self.from_exception(ex)
name = if ex.is_a? DBus::Error
ex.name
else
"org.freedesktop.DBus.Error.Failed"
# ex.class.to_s # RuntimeError is not a valid name, has no dot
end
description = ex.message
msg = self.new(name, description)
msg.add_param(DBus.type("as"), ex.backtrace)
msg
end
end
end # module DBus
ruby-dbus-0.7.2/lib/dbus/type.rb 0000644 0000041 0000041 00000012266 11741120355 016503 0 ustar www-data www-data # dbus/type.rb - module containing low-level D-Bus data type information
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
module DBus
# = D-Bus type module
#
# This module containts the constants of the types specified in the D-Bus
# protocol.
module Type
# The types.
INVALID = 0
BYTE = ?y
BOOLEAN = ?b
INT16 = ?n
UINT16 = ?q
INT32 = ?i
UINT32 = ?u
INT64 = ?x
UINT64 = ?t
DOUBLE = ?d
STRUCT = ?r
ARRAY = ?a
VARIANT = ?v
OBJECT_PATH = ?o
STRING = ?s
SIGNATURE = ?g
DICT_ENTRY = ?e
# Mapping from type number to name.
TypeName = {
INVALID => "INVALID",
BYTE => "BYTE",
BOOLEAN => "BOOLEAN",
INT16 => "INT16",
UINT16 => "UINT16",
INT32 => "INT32",
UINT32 => "UINT32",
INT64 => "INT64",
UINT64 => "UINT64",
DOUBLE => "DOUBLE",
STRUCT => "STRUCT",
ARRAY => "ARRAY",
VARIANT => "VARIANT",
OBJECT_PATH => "OBJECT_PATH",
STRING => "STRING",
SIGNATURE => "SIGNATURE",
DICT_ENTRY => "DICT_ENTRY"
}
# Exception raised when an unknown/incorrect type is encountered.
class SignatureException < Exception
end
# = D-Bus type conversion class
#
# Helper class for representing a D-Bus type.
class Type
# Returns the signature type number.
attr_reader :sigtype
# Return contained member types.
attr_reader :members
# Create a new type instance for type number _sigtype_.
def initialize(sigtype)
if not TypeName.keys.member?(sigtype)
raise SignatureException, "Unknown key in signature: #{sigtype.chr}"
end
@sigtype = sigtype
@members = Array.new
end
# Return the required alignment for the type.
def alignment
{
BYTE => 1,
BOOLEAN => 4,
INT16 => 2,
UINT16 => 2,
INT32 => 4,
UINT32 => 4,
INT64 => 8,
UINT64 => 8,
STRUCT => 8,
DICT_ENTRY => 8,
DOUBLE => 8,
ARRAY => 4,
VARIANT => 1,
OBJECT_PATH => 4,
STRING => 4,
SIGNATURE => 1,
}[@sigtype]
end
# Return a string representation of the type according to the
# D-Bus specification.
def to_s
case @sigtype
when STRUCT
"(" + @members.collect { |t| t.to_s }.join + ")"
when ARRAY
"a" + child.to_s
when DICT_ENTRY
"{" + @members.collect { |t| t.to_s }.join + "}"
else
if not TypeName.keys.member?(@sigtype)
raise NotImplementedError
end
@sigtype.chr
end
end
# Add a new member type _a_.
def <<(a)
if not [STRUCT, ARRAY, DICT_ENTRY].member?(@sigtype)
raise SignatureException
end
raise SignatureException if @sigtype == ARRAY and @members.size > 0
if @sigtype == DICT_ENTRY
if @members.size == 2
raise SignatureException, "Dict entries have exactly two members"
end
if @members.size == 0
if [STRUCT, ARRAY, DICT_ENTRY].member?(a.sigtype)
raise SignatureException, "Dict entry keys must be basic types"
end
end
end
@members << a
end
# Return the first contained member type.
def child
@members[0]
end
def inspect
s = TypeName[@sigtype]
if [STRUCT, ARRAY].member?(@sigtype)
s += ": " + @members.inspect
end
s
end
end # class Type
# = D-Bus type parser class
#
# Helper class to parse a type signature in the protocol.
class Parser
# Create a new parser for the given _signature_.
def initialize(signature)
@signature = signature
@idx = 0
end
# Returns the next character from the signature.
def nextchar
c = @signature[@idx]
@idx += 1
c
end
# Parse one character _c_ of the signature.
def parse_one(c)
res = nil
case c
when ?a
res = Type.new(ARRAY)
child = parse_one(nextchar)
res << child
when ?(
res = Type.new(STRUCT)
while (c = nextchar) != nil and c != ?)
res << parse_one(c)
end
raise SignatureException, "Parse error in #{@signature}" if c == nil
when ?{
res = Type.new(DICT_ENTRY)
while (c = nextchar) != nil and c != ?}
res << parse_one(c)
end
raise SignatureException, "Parse error in #{@signature}" if c == nil
else
res = Type.new(c)
end
res
end
# Parse the entire signature, return a DBus::Type object.
def parse
@idx = 0
ret = Array.new
while (c = nextchar)
ret << parse_one(c)
end
ret
end
end # class Parser
end # module Type
# shortcuts
# Parse a String to a DBus::Type::Type
def type(string_type)
Type::Parser.new(string_type).parse[0]
end
module_function :type
# Make an explicit [Type, value] pair
def variant(string_type, value)
[type(string_type), value]
end
module_function :variant
end # module DBus
ruby-dbus-0.7.2/lib/dbus/matchrule.rb 0000644 0000041 0000041 00000006153 11741120355 017504 0 ustar www-data www-data # This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
module DBus
# Exception raised when an erroneous match rule type is encountered.
class MatchRuleException < Exception
end
# = D-Bus match rule class
#
# FIXME
class MatchRule
# The list of possible match filters. TODO argN, argNpath
FILTERS = [:sender, :interface, :member, :path, :destination, :type]
# The sender filter.
attr_accessor :sender
# The interface filter.
attr_accessor :interface
# The member filter.
attr_accessor :member
# The path filter.
attr_accessor :path
# The destination filter.
attr_accessor :destination
# The type type that is matched.
attr_reader :type
# Create a new match rule
def initialize
@sender = @interface = @member = @path = @destination = @type = nil
end
# Set the message types to filter to type _t_.
# Possible message types are: signal, method_call, method_return, and error.
def type=(t)
if not ['signal', 'method_call', 'method_return', 'error'].member?(t)
raise MatchRuleException, t
end
@type = t
end
# Returns a match rule string version of the object.
# E.g.: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',path='/bar/foo',destination=':452345.34',arg2='bar'"
def to_s
FILTERS.select do |sym|
not method(sym).call.nil?
end.collect do |sym|
"#{sym.to_s}='#{method(sym).call}'"
end.join(",")
end
# Parses a match rule string _s_ and sets the filters on the object.
def from_s(str)
str.split(",").each do |eq|
if eq =~ /^(.*)='([^']*)'$/
# "
name = $1
val = $2
if FILTERS.member?(name.to_sym)
method(name + "=").call(val)
else
raise MatchRuleException, name
end
end
end
self
end
# Sets the match rule to filter for the given _signal_ and the
# given interface _intf_.
def from_signal(intf, signal)
signal = signal.name unless signal.is_a?(String)
self.type = "signal"
self.interface = intf.name
self.member = signal
self.path = intf.object.path
self
end
# Determines whether a message _msg_ matches the match rule.
def match(msg)
if @type
if {Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
Message::METHOD_RETURN => "method_return",
Message::ERROR => "error"}[msg.message_type] != @type
return false
end
end
return false if @interface and @interface != msg.interface
return false if @member and @member != msg.member
return false if @path and @path != msg.path
# FIXME sender and destination are ignored
true
end
end # class MatchRule
end # module D-Bus
ruby-dbus-0.7.2/lib/dbus/introspect.rb 0000644 0000041 0000041 00000040526 11741120355 017714 0 ustar www-data www-data # dbus/introspection.rb - module containing a low-level D-Bus introspection implementation
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
# TODO check if it is slow, make replaceable
require 'rexml/document'
module DBus
# Regular expressions that should match all method names.
MethodSignalRE = /^[A-Za-z][A-Za-z0-9_]*$/
# Regular expressions that should match all interface names.
InterfaceElementRE = /^[A-Za-z][A-Za-z0-9_]*$/
# Exception raised when an unknown signal is used.
class UnknownSignal < Exception
end
# Exception raised when an invalid class definition is encountered.
class InvalidClassDefinition < Exception
end
# = D-Bus interface class
#
# This class is the interface descriptor. In most cases, the Introspect()
# method call instantiates and configures this class for us.
#
# It also is the local definition of interface exported by the program.
# At the client side, see ProxyObjectInterface
class Interface
# The name of the interface. String
attr_reader :name
# The methods that are part of the interface. Hash: Symbol => DBus::Method
attr_reader :methods
# The signals that are part of the interface. Hash: Symbol => Signal
attr_reader :signals
# Creates a new interface with a given _name_.
def initialize(name)
validate_name(name)
@name = name
@methods, @signals = Hash.new, Hash.new
end
# Validates a service _name_.
def validate_name(name)
raise InvalidIntrospectionData if name.bytesize > 255
raise InvalidIntrospectionData if name =~ /^\./ or name =~ /\.$/
raise InvalidIntrospectionData if name =~ /\.\./
raise InvalidIntrospectionData if not name =~ /\./
name.split(".").each do |element|
raise InvalidIntrospectionData if not element =~ InterfaceElementRE
end
end
# Helper method for defining a method _m_.
def define(m)
if m.kind_of?(Method)
@methods[m.name.to_sym] = m
elsif m.kind_of?(Signal)
@signals[m.name.to_sym] = m
end
end
alias :<< :define
# Defines a method with name _id_ and a given _prototype_ in the
# interface.
def define_method(id, prototype)
m = Method.new(id)
m.from_prototype(prototype)
define(m)
end
end # class Interface
# = A formal parameter has a name and a type
class FormalParameter
attr_reader :name
attr_reader :type
def initialize(name, type)
@name = name
@type = type
end
# backward compatibility, deprecated
def [](index)
case index
when 0 then name
when 1 then type
else nil
end
end
end
# = D-Bus interface element class
#
# This is a generic class for entities that are part of the interface
# such as methods and signals.
class InterfaceElement
# The name of the interface element. Symbol
attr_reader :name
# The parameters of the interface element. Array: FormalParameter
attr_reader :params
# Validates element _name_.
def validate_name(name)
if (not name =~ MethodSignalRE) or (name.bytesize > 255)
raise InvalidMethodName, name
end
end
# Creates a new element with the given _name_.
def initialize(name)
validate_name(name.to_s)
@name = name
@params = Array.new
end
# Adds a formal parameter with _name_ and _signature_
# (See also Message#add_param which takes signature+value)
def add_fparam(name, signature)
@params << FormalParameter.new(name, signature)
end
# Deprecated, for backward compatibility
def add_param(name_signature_pair)
add_fparam(*name_signature_pair)
end
end # class InterfaceElement
# = D-Bus interface method class
#
# This is a class representing methods that are part of an interface.
class Method < InterfaceElement
# The list of return values for the method. Array: FormalParameter
attr_reader :rets
# Creates a new method interface element with the given _name_.
def initialize(name)
super(name)
@rets = Array.new
end
# Add a return value _name_ and _signature_.
def add_return(name, signature)
@rets << FormalParameter.new(name, signature)
end
# Add parameter types by parsing the given _prototype_.
def from_prototype(prototype)
prototype.split(/, */).each do |arg|
arg = arg.split(" ")
raise InvalidClassDefinition if arg.size != 2
dir, arg = arg
if arg =~ /:/
arg = arg.split(":")
name, sig = arg
else
sig = arg
end
case dir
when "in"
add_fparam(name, sig)
when "out"
add_return(name, sig)
end
end
self
end
# Return an XML string representation of the method interface elment.
def to_xml
xml = %{\n}
@params.each do |param|
name = param.name ? %{name="#{param.name}" } : ""
xml += %{\n}
end
@rets.each do |param|
name = param.name ? %{name="#{param.name}" } : ""
xml += %{\n}
end
xml += %{\n}
xml
end
end # class Method
# = D-Bus interface signal class
#
# This is a class representing signals that are part of an interface.
class Signal < InterfaceElement
# Add parameter types based on the given _prototype_.
def from_prototype(prototype)
prototype.split(/, */).each do |arg|
if arg =~ /:/
arg = arg.split(":")
name, sig = arg
else
sig = arg
end
add_fparam(name, sig)
end
self
end
# Return an XML string representation of the signal interface elment.
def to_xml
xml = %{\n}
@params.each do |param|
name = param.name ? %{name="#{param.name}" } : ""
xml += %{\n}
end
xml += %{\n}
xml
end
end # class Signal
# = D-Bus introspect XML parser class
#
# This class parses introspection XML of an object and constructs a tree
# of Node, Interface, Method, Signal instances.
class IntrospectXMLParser
# Creates a new parser for XML data in string _xml_.
def initialize(xml)
@xml = xml
end
# return the names of direct subnodes
def parse_subnodes
subnodes = Array.new
d = REXML::Document.new(@xml)
d.elements.each("node/node") do |e|
subnodes << e.attributes["name"]
end
subnodes
end
# return a pair: [list of Interfaces, list of direct subnode names]
def parse
interfaces = Array.new
subnodes = Array.new
t = Time.now
d = REXML::Document.new(@xml)
d.elements.each("node/node") do |e|
subnodes << e.attributes["name"]
end
d.elements.each("node/interface") do |e|
i = Interface.new(e.attributes["name"])
e.elements.each("method") do |me|
m = Method.new(me.attributes["name"])
parse_methsig(me, m)
i << m
end
e.elements.each("signal") do |se|
s = Signal.new(se.attributes["name"])
parse_methsig(se, s)
i << s
end
interfaces << i
end
d = Time.now - t
if d > 2
puts "Some XML took more that two secs to parse. Optimize me!" if $DEBUG
end
[interfaces, subnodes]
end
######################################################################
private
# Parses a method signature XML element _e_ and initialises
# method/signal _m_.
def parse_methsig(e, m)
e.elements.each("arg") do |ae|
name = ae.attributes["name"]
dir = ae.attributes["direction"]
sig = ae.attributes["type"]
if m.is_a?(DBus::Signal)
m.add_fparam(name, sig)
elsif m.is_a?(DBus::Method)
case dir
when "in"
m.add_fparam(name, sig)
when "out"
m.add_return(name, sig)
end
else
raise NotImplementedError, dir
end
end
end
end # class IntrospectXMLParser
# = D-Bus proxy object interface class
#
# A class similar to the normal Interface used as a proxy for remote
# object interfaces.
class ProxyObjectInterface
# The proxied methods contained in the interface.
attr_accessor :methods
# The proxied signals contained in the interface.
attr_accessor :signals
# The proxy object to which this interface belongs.
attr_reader :object
# The name of the interface.
attr_reader :name
# Creates a new proxy interface for the given proxy _object_
# and the given _name_.
def initialize(object, name)
@object, @name = object, name
@methods, @signals = Hash.new, Hash.new
end
# Returns the string representation of the interface (the name).
def to_str
@name
end
# Returns the singleton class of the interface.
def singleton_class
(class << self ; self ; end)
end
# FIXME
def check_for_eval(s)
raise RuntimeError, "invalid internal data '#{s}'" if not s.to_s =~ /^[A-Za-z0-9_]*$/
end
# FIXME
def check_for_quoted_eval(s)
raise RuntimeError, "invalid internal data '#{s}'" if not s.to_s =~ /^[^"]+$/
end
# Defines a method on the interface from the Method descriptor _m_.
def define_method_from_descriptor(m)
check_for_eval(m.name)
check_for_quoted_eval(@name)
methdef = "def #{m.name}("
methdef += (0..(m.params.size - 1)).to_a.collect { |n|
"arg#{n}"
}.push("&reply_handler").join(", ")
methdef += %{)
msg = Message.new(Message::METHOD_CALL)
msg.path = @object.path
msg.interface = "#{@name}"
msg.destination = @object.destination
msg.member = "#{m.name}"
msg.sender = @object.bus.unique_name
}
idx = 0
m.params.each do |fpar|
par = fpar.type
check_for_quoted_eval(par)
# This is the signature validity check
Type::Parser.new(par).parse
methdef += %{
msg.add_param("#{par}", arg#{idx})
}
idx += 1
end
methdef += "
@object.bus.send_sync_or_async(msg, &reply_handler)
end
"
singleton_class.class_eval(methdef)
@methods[m.name] = m
end
# Defines a signal from the descriptor _s_.
def define_signal_from_descriptor(s)
@signals[s.name] = s
end
# Defines a signal or method based on the descriptor _m_.
def define(m)
if m.kind_of?(Method)
define_method_from_descriptor(m)
elsif m.kind_of?(Signal)
define_signal_from_descriptor(m)
end
end
# Defines a proxied method on the interface.
def define_method(methodname, prototype)
m = Method.new(methodname)
m.from_prototype(prototype)
define(m)
end
# Registers a handler (code block) for a signal with _name_ arriving
# over the given _bus_. If no block is given, the signal is unregistered.
def on_signal(bus, name, &block)
mr = DBus::MatchRule.new.from_signal(self, name)
if block.nil?
bus.remove_match(mr)
else
bus.add_match(mr) { |msg| block.call(*msg.params) }
end
end
PROPERTY_INTERFACE = "org.freedesktop.DBus.Properties"
# Read a property.
def [](propname)
self.object[PROPERTY_INTERFACE].Get(self.name, propname)[0]
end
# Write a property.
def []=(propname, value)
self.object[PROPERTY_INTERFACE].Set(self.name, propname, value)
end
# Read all properties at once, as a hash.
def all_properties
self.object[PROPERTY_INTERFACE].GetAll(self.name)[0]
end
end # class ProxyObjectInterface
# D-Bus proxy object class
#
# Class representing a remote object in an external application.
# Typically, calling a method on an instance of a ProxyObject sends a message
# over the bus so that the method is executed remotely on the correctponding
# object.
class ProxyObject
# The names of direct subnodes of the object in the tree.
attr_accessor :subnodes
# Flag determining whether the object has been introspected.
attr_accessor :introspected
# The (remote) destination of the object.
attr_reader :destination
# The path to the object.
attr_reader :path
# The bus the object is reachable via.
attr_reader :bus
# The default interface of the object, as String.
attr_accessor :default_iface
# Creates a new proxy object living on the given _bus_ at destination _dest_
# on the given _path_.
def initialize(bus, dest, path)
@bus, @destination, @path = bus, dest, path
@interfaces = Hash.new
@subnodes = Array.new
end
# Returns the interfaces of the object.
def interfaces
@interfaces.keys
end
# Retrieves an interface of the proxy object (ProxyObjectInterface instance).
def [](intfname)
@interfaces[intfname]
end
# Maps the given interface name _intfname_ to the given interface _intf.
def []=(intfname, intf)
@interfaces[intfname] = intf
end
# Introspects the remote object. Allows you to find and select
# interfaces on the object.
def introspect
# Synchronous call here.
xml = @bus.introspect_data(@destination, @path)
ProxyObjectFactory.introspect_into(self, xml)
xml
end
# Returns whether the object has an interface with the given _name_.
def has_iface?(name)
raise "Cannot call has_iface? if not introspected" if not @introspected
@interfaces.member?(name)
end
# Registers a handler, the code block, for a signal with the given _name_.
# It uses _default_iface_ which must have been set.
def on_signal(name, &block)
if @default_iface and has_iface?(@default_iface)
@interfaces[@default_iface].on_signal(@bus, name, &block)
else
# TODO improve
raise NoMethodError
end
end
####################################################
private
# Handles all unkown methods, mostly to route method calls to the
# default interface.
def method_missing(name, *args, &reply_handler)
if @default_iface and has_iface?(@default_iface)
begin
@interfaces[@default_iface].method(name).call(*args, &reply_handler)
rescue NameError => e
# interesting, foo.method("unknown")
# raises NameError, not NoMethodError
raise unless e.to_s =~ /undefined method `#{name}'/
# BTW e.exception("...") would preserve the class.
raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
end
else
# TODO distinguish:
# - di not specified
#TODO
# - di is specified but not found in introspection data
raise NoMethodError, "undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
end
end
end # class ProxyObject
# = D-Bus proxy object factory class
#
# Class that generates and sets up a proxy object based on introspection data.
class ProxyObjectFactory
# Creates a new proxy object factory for the given introspection XML _xml_,
# _bus_, destination _dest_, and _path_.
def initialize(xml, bus, dest, path)
@xml, @bus, @path, @dest = xml, bus, path, dest
end
# Investigates the sub-nodes of the proxy object _po_ based on the
# introspection XML data _xml_ and sets them up recursively.
def ProxyObjectFactory.introspect_into(po, xml)
intfs, po.subnodes = IntrospectXMLParser.new(xml).parse
intfs.each do |i|
poi = ProxyObjectInterface.new(po, i.name)
i.methods.each_value { |m| poi.define(m) }
i.signals.each_value { |s| poi.define(s) }
po[i.name] = poi
end
po.introspected = true
end
# Generates, sets up and returns the proxy object.
def build
po = ProxyObject.new(@bus, @dest, @path)
ProxyObjectFactory.introspect_into(po, @xml)
po
end
end # class ProxyObjectFactory
end # module DBus
ruby-dbus-0.7.2/lib/dbus/error.rb 0000644 0000041 0000041 00000002703 11741120355 016646 0 ustar www-data www-data # error.rb
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
module DBus
# Represents a D-Bus Error, both on the client and server side.
class Error < StandardError
# error_name. +message+ is inherited from +Exception+
attr_reader :name
# for received errors, the raw D-Bus message
attr_reader :dbus_message
# If +msg+ is a +DBus::Message+, its contents is used for initialization.
# Otherwise, +msg+ is taken as a string and +name+ is used.
def initialize(msg, name = "org.freedesktop.DBus.Error.Failed")
if msg.is_a? DBus::Message
@dbus_message = msg
@name = msg.error_name
super(msg.params[0]) # or nil
if msg.params[1].is_a? Array
set_backtrace msg.params[1]
end
else
@name = name
super(msg)
end
# TODO validate error name
end
end # class Error
# raise DBus.error, "message"
# raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{n} is occupied"
def error(name = "org.freedesktop.DBus.Error.Failed")
# message will be set by Kernel.raise
DBus::Error.new(nil, name)
end
module_function :error
end # module DBus
ruby-dbus-0.7.2/lib/dbus/bus.rb 0000644 0000041 0000041 00000061753 11741120355 016320 0 ustar www-data www-data # dbus.rb - Module containing the low-level D-Bus implementation
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
require 'socket'
require 'thread'
require 'singleton'
require 'fcntl'
# = D-Bus main module
#
# Module containing all the D-Bus modules and classes.
module DBus
# This represents a remote service. It should not be instantiated directly
# Use Bus::service()
class Service
# The service name.
attr_reader :name
# The bus the service is running on.
attr_reader :bus
# The service root (FIXME).
attr_reader :root
# Create a new service with a given _name_ on a given _bus_.
def initialize(name, bus)
@name, @bus = name, bus
@root = Node.new("/")
end
# Determine whether the service name already exists.
def exists?
bus.proxy.ListNames[0].member?(@name)
end
# Perform an introspection on all the objects on the service
# (starting recursively from the root).
def introspect
if block_given?
raise NotImplementedError
else
rec_introspect(@root, "/")
end
self
end
# Retrieves an object (ProxyObject) at the given _path_.
def object(path)
node = get_node(path, true)
if node.object.nil?
node.object = ProxyObject.new(@bus, @name, path)
end
node.object
end
# Export an object _obj_ (an DBus::Object subclass instance).
def export(obj)
obj.service = self
get_node(obj.path, true).object = obj
end
# Undo exporting an object _obj_.
# Raises ArgumentError if it is not a DBus::Object.
# Returns the object, or false if _obj_ was not exported.
def unexport(obj)
raise ArgumentError.new("DBus::Service#unexport() expects a DBus::Object argument") unless obj.kind_of?(DBus::Object)
return false unless obj.path
pathSep = obj.path.rindex("/") #last path seperator
parent_path = obj.path[1..pathSep-1]
node_name = obj.path[pathSep+1..-1]
parent_node = get_node(parent_path, false)
return false unless parent_node
obj.service = nil
parent_node.delete(node_name)
end
# Get the object node corresponding to the given _path_. if _create_ is
# true, the the nodes in the path are created if they do not already exist.
def get_node(path, create = false)
n = @root
path.sub(/^\//, "").split("/").each do |elem|
if not n[elem]
if not create
return nil
else
n[elem] = Node.new(elem)
end
end
n = n[elem]
end
if n.nil?
puts "Warning, unknown object #{path}" if $DEBUG
end
n
end
#########
private
#########
# Perform a recursive retrospection on the given current _node_
# on the given _path_.
def rec_introspect(node, path)
xml = bus.introspect_data(@name, path)
intfs, subnodes = IntrospectXMLParser.new(xml).parse
subnodes.each do |nodename|
subnode = node[nodename] = Node.new(nodename)
if path == "/"
subpath = "/" + nodename
else
subpath = path + "/" + nodename
end
rec_introspect(subnode, subpath)
end
if intfs.size > 0
node.object = ProxyObjectFactory.new(xml, @bus, @name, path).build
end
end
end
# = Object path node class
#
# Class representing a node on an object path.
class Node < Hash
# The D-Bus object contained by the node.
attr_accessor :object
# The name of the node.
attr_reader :name
# Create a new node with a given _name_.
def initialize(name)
@name = name
@object = nil
end
# Return an XML string representation of the node.
# It is shallow, not recursing into subnodes
def to_xml
xml = '
'
self.each_pair do |k, v|
xml += ""
end
if @object
@object.intfs.each_pair do |k, v|
xml += %{\n}
v.methods.each_value { |m| xml += m.to_xml }
v.signals.each_value { |m| xml += m.to_xml }
xml +="\n"
end
end
xml += ''
xml
end
# Return inspect information of the node.
def inspect
# Need something here
""
end
# Return instance inspect information, used by Node#inspect.
def sub_inspect
s = ""
if not @object.nil?
s += "%x " % @object.object_id
end
s + "{" + keys.collect { |k| "#{k} => #{self[k].sub_inspect}" }.join(",") + "}"
end
end # class Inspect
# FIXME: rename Connection to Bus?
# D-Bus main connection class
#
# Main class that maintains a connection to a bus and can handle incoming
# and outgoing messages.
class Connection
# The unique name (by specification) of the message.
attr_reader :unique_name
# The socket that is used to connect with the bus.
attr_reader :socket
# Create a new connection to the bus for a given connect _path_. _path_
# format is described in the D-Bus specification:
# http://dbus.freedesktop.org/doc/dbus-specification.html#addresses
# and is something like:
# "transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2"
# e.g. "unix:path=/tmp/dbus-test" or "tcp:host=localhost,port=2687"
def initialize(path)
@path = path
@unique_name = nil
@buffer = ""
@method_call_replies = Hash.new
@method_call_msgs = Hash.new
@signal_matchrules = Hash.new
@proxy = nil
@object_root = Node.new("/")
@is_tcp = false
end
# Connect to the bus and initialize the connection.
def connect
addresses = @path.split ";"
# connect to first one that succeeds
worked = addresses.find do |a|
transport, keyvaluestring = a.split ":"
kv_list = keyvaluestring.split ","
kv_hash = Hash.new
kv_list.each do |kv|
key, escaped_value = kv.split "="
value = escaped_value.gsub(/%(..)/) {|m| [$1].pack "H2" }
kv_hash[key] = value
end
case transport
when "unix"
connect_to_unix kv_hash
when "tcp"
connect_to_tcp kv_hash
else
# ignore, report?
end
end
worked
# returns the address that worked or nil.
# how to report failure?
end
# Connect to a bus over tcp and initialize the connection.
def connect_to_tcp(params)
#check if the path is sufficient
if params.key?("host") and params.key?("port")
begin
#initialize the tcp socket
@socket = TCPSocket.new(params["host"],params["port"].to_i)
@socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
init_connection
@is_tcp = true
rescue
puts "Error: Could not establish connection to: #{@path}, will now exit."
exit(0) #a little harsh
end
else
#Danger, Will Robinson: the specified "path" is not usable
puts "Error: supplied path: #{@path}, unusable! sorry."
end
end
# Connect to an abstract unix bus and initialize the connection.
def connect_to_unix(params)
@socket = Socket.new(Socket::Constants::PF_UNIX,Socket::Constants::SOCK_STREAM, 0)
@socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
if ! params['abstract'].nil?
if HOST_END == LIL_END
sockaddr = "\1\0\0#{params['abstract']}"
else
sockaddr = "\0\1\0#{params['abstract']}"
end
elsif ! params['path'].nil?
sockaddr = Socket.pack_sockaddr_un(params['path'])
end
@socket.connect(sockaddr)
init_connection
end
# Send the buffer _buf_ to the bus using Connection#writel.
def send(buf)
@socket.write(buf) unless @socket.nil?
end
# Tell a bus to register itself on the glib main loop
def glibize
require 'glib2'
# Circumvent a ruby-glib bug
@channels ||= Array.new
gio = GLib::IOChannel.new(@socket.fileno)
@channels << gio
gio.add_watch(GLib::IOChannel::IN) do |c, ch|
update_buffer
messages.each do |msg|
process(msg)
end
true
end
end
# FIXME: describe the following names, flags and constants.
# See DBus spec for definition
NAME_FLAG_ALLOW_REPLACEMENT = 0x1
NAME_FLAG_REPLACE_EXISTING = 0x2
NAME_FLAG_DO_NOT_QUEUE = 0x4
REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x1
REQUEST_NAME_REPLY_IN_QUEUE = 0x2
REQUEST_NAME_REPLY_EXISTS = 0x3
REQUEST_NAME_REPLY_ALREADY_OWNER = 0x4
DBUSXMLINTRO = '
'
# This apostroph is for syntax highlighting editors confused by above xml: "
# Send a _message_.
# If _reply_handler_ is not given, wait for the reply
# and return the reply, or raise the error.
# If _reply_handler_ is given, it will be called when the reply
# eventually arrives, with the reply message as the 1st param
# and its params following
def send_sync_or_async(message, &reply_handler)
ret = nil
if reply_handler.nil?
send_sync(message) do |rmsg|
if rmsg.is_a?(Error)
raise rmsg
else
ret = rmsg.params
end
end
else
on_return(message) do |rmsg|
if rmsg.is_a?(Error)
reply_handler.call(rmsg)
else
reply_handler.call(rmsg, * rmsg.params)
end
end
send(message.marshall)
end
ret
end
def introspect_data(dest, path, &reply_handler)
m = DBus::Message.new(DBus::Message::METHOD_CALL)
m.path = path
m.interface = "org.freedesktop.DBus.Introspectable"
m.destination = dest
m.member = "Introspect"
m.sender = unique_name
if reply_handler.nil?
send_sync_or_async(m).first
else
send_sync_or_async(m) do |*args|
# TODO test async introspection, is it used at all?
args.shift # forget the message, pass only the text
reply_handler.call(*args)
nil
end
end
end
# Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
# _dest_ is the service and _path_ the object path you want to introspect
# If a code block is given, the introspect call in asynchronous. If not
# data is returned
#
# FIXME: link to ProxyObject data definition
# The returned object is a ProxyObject that has methods you can call to
# issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN
def introspect(dest, path)
if not block_given?
# introspect in synchronous !
data = introspect_data(dest, path)
pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
return pof.build
else
introspect_data(dest, path) do |async_data|
yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build)
end
end
end
# Exception raised when a service name is requested that is not available.
class NameRequestError < Exception
end
# Attempt to request a service _name_.
#
# FIXME, NameRequestError cannot really be rescued as it will be raised
# when dispatching a later call. Rework the API to better match the spec.
def request_service(name)
# Use RequestName, but asynchronously!
# A synchronous call would not work with service activation, where
# method calls to be serviced arrive before the reply for RequestName
# (Ticket#29).
proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r|
if rmsg.is_a?(Error) # check and report errors first
raise rmsg
elsif r != REQUEST_NAME_REPLY_PRIMARY_OWNER
raise NameRequestError
end
end
@service = Service.new(name, self)
@service
end
# Set up a ProxyObject for the bus itself, since the bus is introspectable.
# Returns the object.
def proxy
if @proxy == nil
path = "/org/freedesktop/DBus"
dest = "org.freedesktop.DBus"
pof = DBus::ProxyObjectFactory.new(DBUSXMLINTRO, self, dest, path)
@proxy = pof.build["org.freedesktop.DBus"]
end
@proxy
end
# Fill (append) the buffer from data that might be available on the
# socket.
def update_buffer
@buffer += @socket.read_nonblock(MSG_BUF_SIZE)
rescue EOFError
raise # the caller expects it
rescue Exception => e
puts "Oops:", e
raise if @is_tcp # why?
puts "WARNING: read_nonblock failed, falling back to .recv"
@buffer += @socket.recv(MSG_BUF_SIZE)
end
# Get one message from the bus and remove it from the buffer.
# Return the message.
def pop_message
return nil if @buffer.empty?
ret = nil
begin
ret, size = Message.new.unmarshall_buffer(@buffer)
@buffer.slice!(0, size)
rescue IncompleteBufferException => e
# fall through, let ret be null
end
ret
end
# Retrieve all the messages that are currently in the buffer.
def messages
ret = Array.new
while msg = pop_message
ret << msg
end
ret
end
# The buffer size for messages.
MSG_BUF_SIZE = 4096
# Update the buffer and retrieve all messages using Connection#messages.
# Return the messages.
def poll_messages
ret = nil
r, d, d = IO.select([@socket], nil, nil, 0)
if r and r.size > 0
update_buffer
end
messages
end
# Wait for a message to arrive. Return it once it is available.
def wait_for_message
if @socket.nil?
puts "ERROR: Can't wait for messages, @socket is nil."
return
end
ret = pop_message
while ret == nil
r, d, d = IO.select([@socket])
if r and r[0] == @socket
update_buffer
ret = pop_message
end
end
ret
end
# Send a message _m_ on to the bus. This is done synchronously, thus
# the call will block until a reply message arrives.
def send_sync(m, &retc) # :yields: reply/return message
return if m.nil? #check if somethings wrong
send(m.marshall)
@method_call_msgs[m.serial] = m
@method_call_replies[m.serial] = retc
retm = wait_for_message
return if retm.nil? #check if somethings wrong
process(retm)
while @method_call_replies.has_key? m.serial
retm = wait_for_message
process(retm)
end
end
# Specify a code block that has to be executed when a reply for
# message _m_ is received.
def on_return(m, &retc)
# Have a better exception here
if m.message_type != Message::METHOD_CALL
raise "on_return should only get method_calls"
end
@method_call_msgs[m.serial] = m
@method_call_replies[m.serial] = retc
end
# Asks bus to send us messages matching mr, and execute slot when
# received
def add_match(mr, &slot)
# check this is a signal.
mrs = mr.to_s
puts "#{@signal_matchrules.size} rules, adding #{mrs.inspect}" if $DEBUG
# don't ask for the same match if we override it
unless @signal_matchrules.key?(mrs)
puts "Asked for a new match" if $DEBUG
proxy.AddMatch(mrs)
end
@signal_matchrules[mrs] = slot
end
def remove_match(mr)
mrs = mr.to_s
unless @signal_matchrules.delete(mrs).nil?
# don't remove nonexisting matches.
# FIXME if we do try, the Error.MatchRuleNotFound is *not* raised
# and instead is reported as "no return code for nil"
proxy.RemoveMatch(mrs)
end
end
# Process a message _m_ based on its type.
def process(m)
return if m.nil? #check if somethings wrong
case m.message_type
when Message::ERROR, Message::METHOD_RETURN
raise InvalidPacketException if m.reply_serial == nil
mcs = @method_call_replies[m.reply_serial]
if not mcs
puts "DEBUG: no return code for mcs: #{mcs.inspect} m: #{m.inspect}" if $DEBUG
else
if m.message_type == Message::ERROR
mcs.call(Error.new(m))
else
mcs.call(m)
end
@method_call_replies.delete(m.reply_serial)
@method_call_msgs.delete(m.reply_serial)
end
when DBus::Message::METHOD_CALL
if m.path == "/org/freedesktop/DBus"
puts "DEBUG: Got method call on /org/freedesktop/DBus" if $DEBUG
end
node = @service.get_node(m.path)
if not node
reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject",
"Object #{m.path} doesn't exist")
send(reply.marshall)
# handle introspectable as an exception:
elsif m.interface == "org.freedesktop.DBus.Introspectable" and
m.member == "Introspect"
reply = Message.new(Message::METHOD_RETURN).reply_to(m)
reply.sender = @unique_name
reply.add_param(Type::STRING, node.to_xml)
send(reply.marshall)
else
obj = node.object
return if obj.nil? # FIXME, sends no reply
obj.dispatch(m) if obj
end
when DBus::Message::SIGNAL
# the signal can match multiple different rules
@signal_matchrules.each do |mrs, slot|
if DBus::MatchRule.new.from_s(mrs).match(m)
slot.call(m)
end
end
else
puts "DEBUG: Unknown message type: #{m.message_type}" if $DEBUG
end
end
# Retrieves the Service with the given _name_.
def service(name)
# The service might not exist at this time so we cannot really check
# anything
Service.new(name, self)
end
alias :[] :service
# Emit a signal event for the given _service_, object _obj_, interface
# _intf_ and signal _sig_ with arguments _args_.
def emit(service, obj, intf, sig, *args)
m = Message.new(DBus::Message::SIGNAL)
m.path = obj.path
m.interface = intf.name
m.member = sig.name
m.sender = service.name
i = 0
sig.params.each do |par|
m.add_param(par.type, args[i])
i += 1
end
send(m.marshall)
end
###########################################################################
private
# Send a hello messages to the bus to let it know we are here.
def send_hello
m = Message.new(DBus::Message::METHOD_CALL)
m.path = "/org/freedesktop/DBus"
m.destination = "org.freedesktop.DBus"
m.interface = "org.freedesktop.DBus"
m.member = "Hello"
send_sync(m) do |rmsg|
@unique_name = rmsg.destination
puts "Got hello reply. Our unique_name is #{@unique_name}" if $DEBUG
end
@service = Service.new(@unique_name, self)
end
# Initialize the connection to the bus.
def init_connection
@client = Client.new(@socket)
@client.authenticate
end
end # class Connection
# = D-Bus session bus class
#
# The session bus is a session specific bus (mostly for desktop use).
#
# Use SessionBus, the non-singleton ASessionBus is
# for the test suite.
class ASessionBus < Connection
# Get the the default session bus.
def initialize
super(ENV["DBUS_SESSION_BUS_ADDRESS"] || address_from_file)
connect
send_hello
end
def address_from_file
f = File.new("/var/lib/dbus/machine-id")
machine_id = f.readline.chomp
f.close
display = ENV["DISPLAY"].gsub(/.*:([0-9]*).*/, '\1')
File.open(ENV["HOME"] + "/.dbus/session-bus/#{machine_id}-#{display}").each do |line|
if line =~ /^DBUS_SESSION_BUS_ADDRESS=(.*)/
return $1
end
end
end
end
# See ASessionBus
class SessionBus < ASessionBus
include Singleton
end
# = D-Bus system bus class
#
# The system bus is a system-wide bus mostly used for global or
# system usages.
#
# Use SystemBus, the non-singleton ASystemBus is
# for the test suite.
class ASystemBus < Connection
# Get the default system bus.
def initialize
super(SystemSocketName)
connect
send_hello
end
end
# = D-Bus remote (TCP) bus class
#
# This class may be used when connecting to remote (listening on a TCP socket)
# busses. You can also use it to connect to other non-standard path busses.
#
# The specified socket_name should look like this:
# (for TCP) tcp:host=127.0.0.1,port=2687
# (for Unix-socket) unix:path=/tmp/my_funky_bus_socket
#
# you'll need to take care about authentification then, more info here:
# http://github.com/pangdudu/ruby-dbus/blob/master/README.rdoc
class RemoteBus < Connection
# Get the remote bus.
def initialize socket_name
super(socket_name)
connect
send_hello
end
end
# See ASystemBus
class SystemBus < ASystemBus
include Singleton
end
# Shortcut for the SystemBus instance
def DBus.system_bus
SystemBus.instance
end
# Shortcut for the SessionBus instance
def DBus.session_bus
SessionBus.instance
end
# = Main event loop class.
#
# Class that takes care of handling message and signal events
# asynchronously. *Note:* This is a native implement and therefore does
# not integrate with a graphical widget set main loop.
class Main
# Create a new main event loop.
def initialize
@buses = Hash.new
@quitting = false
end
# Add a _bus_ to the list of buses to watch for events.
def <<(bus)
@buses[bus.socket] = bus
end
# Quit a running main loop, to be used eg. from a signal handler
def quit
@quitting = true
end
# Run the main loop. This is a blocking call!
def run
# before blocking, empty the buffers
# https://bugzilla.novell.com/show_bug.cgi?id=537401
@buses.each_value do |b|
while m = b.pop_message
b.process(m)
end
end
while not @quitting and not @buses.empty?
ready = IO.select(@buses.keys, [], [], 5) # timeout 5 seconds
next unless ready # timeout exceeds so continue unless quitting
ready.first.each do |socket|
b = @buses[socket]
begin
b.update_buffer
rescue EOFError, SystemCallError
@buses.delete socket # this bus died
next
end
while m = b.pop_message
b.process(m)
end
end
end
end
end # class Main
end # module DBus
ruby-dbus-0.7.2/lib/dbus/auth.rb 0000644 0000041 0000041 00000020014 11741120355 016451 0 ustar www-data www-data # This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
$debug = $DEBUG #it's all over the state machine
require 'rbconfig'
module DBus
# Exception raised when authentication fails somehow.
class AuthenticationFailed < Exception
end
# = General class for authentication.
class Authenticator
# Returns the name of the authenticator.
def name
self.class.to_s.upcase.sub(/.*::/, "")
end
end
# = External authentication class
#
# Class for 'external' type authentication.
class External < Authenticator
# Performs the authentication.
def authenticate
# Take the user id (eg integer 1000) make a string out of it "1000", take
# each character and determin hex value "1" => 0x31, "0" => 0x30. You
# obtain for "1000" => 31303030 This is what the server is expecting.
# Why? I dunno. How did I come to that conclusion? by looking at rbus
# code. I have no idea how he found that out.
return Process.uid.to_s.split(//).collect { |a| "%x" % a[0].ord }.join
end
end
# = Authentication class using SHA1 crypto algorithm
#
# Class for 'CookieSHA1' type authentication.
# Implements the AUTH DBUS_COOKIE_SHA1 mechanism.
class DBusCookieSHA1 < Authenticator
#the autenticate method (called in stage one of authentification)
def authenticate
require 'etc'
#number of retries we have for auth
@retries = 1
return "#{hex_encode(Etc.getlogin)}" #server expects it to be binary
end
#returns the modules name
def name
return 'DBUS_COOKIE_SHA1'
end
#handles the interesting crypto stuff, check the rbus-project for more info: http://rbus.rubyforge.org/
def data(hexdata)
require 'digest/sha1'
data = hex_decode(hexdata)
# name of cookie file, id of cookie in file, servers random challenge
context, id, s_challenge = data.split(' ')
# Random client challenge
c_challenge = Array.new(s_challenge.bytesize/2).map{|obj|obj=rand(255).to_s}.join
# Search cookie file for id
path = File.join(ENV['HOME'], '.dbus-keyrings', context)
puts "DEBUG: path: #{path.inspect}" if $debug
File.foreach(path) do |line|
if line.index(id) == 0
# Right line of file, read cookie
cookie = line.split(' ')[2].chomp
puts "DEBUG: cookie: #{cookie.inspect}" if $debug
# Concatenate and encrypt
to_encrypt = [s_challenge, c_challenge, cookie].join(':')
sha = Digest::SHA1.hexdigest(to_encrypt)
#the almighty tcp server wants everything hex encoded
hex_response = hex_encode("#{c_challenge} #{sha}")
# Return response
response = [:AuthOk, hex_response]
return response
end
end
#a little rescue magic
unless @retries <= 0
puts "ERROR: Could not auth, will now exit."
puts "ERROR: Unable to locate cookie, retry in 1 second."
@retries -= 1
sleep 1
data(hexdata)
end
end
# encode plain to hex
def hex_encode(plain)
return nil if plain.nil?
plain.to_s.unpack('H*')[0]
end
# decode hex to plain
def hex_decode(encoded)
encoded.scan(/[[:xdigit:]]{2}/).map{|h|h.hex.chr}.join
end
end #DBusCookieSHA1 class ends here
# Note: this following stuff is tested with External authenticator only!
# = Authentication client class.
#
# Class tha performs the actional authentication.
class Client
# Create a new authentication client.
def initialize(socket)
@socket = socket
@state = nil
@auth_list = [External,DBusCookieSHA1]
end
# Start the authentication process.
def authenticate
if (RbConfig::CONFIG["target_os"] =~ /bsd/)
@socket.sendmsg(0.chr, 0, nil, [:SOCKET, :SCM_CREDS, ""])
else
@socket.write(0.chr)
end
next_authenticator
@state = :Starting
while @state != :Authenticated
r = next_state
return r if not r
end
true
end
##########
private
##########
# Send an authentication method _meth_ with arguments _args_ to the
# server.
def send(meth, *args)
o = ([meth] + args).join(" ")
@socket.write(o + "\r\n")
end
# Try authentication using the next authenticator.
def next_authenticator
begin
raise AuthenticationFailed if @auth_list.size == 0
@authenticator = @auth_list.shift.new
auth_msg = ["AUTH", @authenticator.name, @authenticator.authenticate]
puts "DEBUG: auth_msg: #{auth_msg.inspect}" if $debug
send(auth_msg)
rescue AuthenticationFailed
@socket.close
raise
end
end
# Read data (a buffer) from the bus until CR LF is encountered.
# Return the buffer without the CR LF characters.
def next_msg
data,crlf = "","\r\n"
left = 1024 #1024 byte, no idea if it's ever getting bigger
while left > 0
buf = @socket.read( left > 1 ? 1 : left )
break if buf.nil?
left -= buf.bytesize
data += buf
break if data.include? crlf #crlf means line finished, the TCP socket keeps on listening, so we break
end
readline = data.chomp.split(" ")
puts "DEBUG: readline: #{readline.inspect}" if $debug
return readline
end
=begin
# Read data (a buffer) from the bus until CR LF is encountered.
# Return the buffer without the CR LF characters.
def next_msg
@socket.readline.chomp.split(" ")
end
=end
# Try to reach the next state based on the current state.
def next_state
msg = next_msg
if @state == :Starting
puts "DEBUG: :Starting msg: #{msg[0].inspect}" if $debug
case msg[0]
when "OK"
@state = :WaitingForOk
when "CONTINUE"
@state = :WaitingForData
when "REJECTED" #needed by tcp, unix-path/abstract doesn't get here
@state = :WaitingForData
end
end
puts "DEBUG: state: #{@state}" if $debug
case @state
when :WaitingForData
puts "DEBUG: :WaitingForData msg: #{msg[0].inspect}" if $debug
case msg[0]
when "DATA"
chall = msg[1]
resp, chall = @authenticator.data(chall)
puts "DEBUG: :WaitingForData/DATA resp: #{resp.inspect}" if $debug
case resp
when :AuthContinue
send("DATA", chall)
@state = :WaitingForData
when :AuthOk
send("DATA", chall)
@state = :WaitingForOk
when :AuthError
send("ERROR")
@state = :WaitingForData
end
when "REJECTED"
next_authenticator
@state = :WaitingForData
when "ERROR"
send("CANCEL")
@state = :WaitingForReject
when "OK"
send("BEGIN")
@state = :Authenticated
else
send("ERROR")
@state = :WaitingForData
end
when :WaitingForOk
puts "DEBUG: :WaitingForOk msg: #{msg[0].inspect}" if $debug
case msg[0]
when "OK"
send("BEGIN")
@state = :Authenticated
when "REJECT"
next_authenticator
@state = :WaitingForData
when "DATA", "ERROR"
send("CANCEL")
@state = :WaitingForReject
else
send("ERROR")
@state = :WaitingForOk
end
when :WaitingForReject
puts "DEBUG: :WaitingForReject msg: #{msg[0].inspect}" if $debug
case msg[0]
when "REJECT"
next_authenticator
@state = :WaitingForOk
else
@socket.close
return false
end
end
return true
end # def next_state
end # class Client
end # module D-Bus
ruby-dbus-0.7.2/lib/dbus.rb 0000644 0000041 0000041 00000004206 11741120355 015515 0 ustar www-data www-data # dbus.rb - Module containing the low-level D-Bus implementation
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License, version 2.1 as published by the Free Software Foundation.
# See the file "COPYING" for the exact licensing terms.
require 'dbus/type'
require 'dbus/introspect'
require 'dbus/error'
require 'dbus/export'
require 'dbus/bus.rb'
require 'dbus/marshall'
require 'dbus/message'
require 'dbus/matchrule'
require 'dbus/auth'
require 'socket'
require 'thread'
unless 0.respond_to?(:ord)
# Backward compatibility with Ruby 1.8.6, see http://www.pubbs.net/ruby/200907/65871/
class Integer
def ord; self; end
end
end
# = D-Bus main module
#
# Module containing all the D-Bus modules and classes.
module DBus
# Default socket name for the system bus.
SystemSocketName = "unix:path=/var/run/dbus/system_bus_socket"
# Byte signifying big endianness.
BIG_END = ?B
# Byte signifying little endianness.
LIL_END = ?l
# Byte signifying the host's endianness.
HOST_END = if [0x01020304].pack("L").unpack("V")[0] == 0x01020304
LIL_END
else
BIG_END
end
# General exceptions.
# Exception raised when an invalid packet is encountered.
class InvalidPacketException < Exception
end
# Exception raised when there is a problem with a type (may be unknown or
# mismatch).
class TypeException < Exception
end
# Exception raised when an unmarshalled buffer is truncated and
# incomplete.
class IncompleteBufferException < Exception
end
# Exception raised when a method has not been implemented (yet).
class MethodNotImplemented < Exception
end
# Exception raised when a method is invoked with invalid
# parameters (wrong number or type).
class InvalidParameters < Exception
end
# Exception raised when an invalid method name is used.
class InvalidMethodName < Exception
end
# Exception raised when invalid introspection data is parsed/used.
class InvalidIntrospectionData < Exception
end
end # module DBus
ruby-dbus-0.7.2/NEWS 0000644 0000041 0000041 00000013151 11741120355 014163 0 ustar www-data www-data = Ruby D-Bus NEWS
Note about bug numbers:
Ticket#1 - https://trac.luon.net/ruby-dbus/ticket/1
Issue#1 - http://github.com/mvidner/ruby-dbus/issues#issue/1
bnc#1 - https://bugzilla.novell.com/show_bug.cgi?id=1
== Ruby D-Bus 0.7.2 - 2012-04-05
A brown-paper-bag release.
Bug fixes:
* Fixed "undefined local variable or method `continue'" in
DBus::Main#run when a service becomes idle (by Ravil Bayramgalin)
== Ruby D-Bus 0.7.1 - 2012-04-04
Bug fixes:
* Fixed calling asynchronous methods on the default interface (Issue#13,
by Eugene Korbut).
* Fixed Main#quit to really quit the loop (by Josef Reidinger)
* Unbundled files from Active Support (by Bohuslav Kabrda)
== Ruby D-Bus 0.7.0 - 2011-07-26
Features:
* Added ASystemBus and ASessionBus, non-singletons useful in tests
and threads.
Bug fixes:
* Fixed handling of multibyte strings (Issue#8, by Takayuki YAMAGUCHI).
* Allow reopening of a dbus_interface declaration (Issue#9, by T. YAMAGUCHI).
* Fixed ruby-1.9.2 compatibility again (Issue#12).
* Fixed authentication on BSD (Issue#11, by Jonathan Walker)
* Fixed exiting a nested event loop for synchronous calls
(reported by Timo Warns).
* Fixed introspection calls leaking reply handlers.
* "rake test" now works, doing what was called "rake env:test"
== Ruby D-Bus 0.6.0 - 2010-12-11
Features:
* Clients can access properties conveniently (Ticket#28).
Bug fixes:
* Service won't crash whan handling an unknown method or interface (Ticket#31).
* Don't send an invalid error name when it originates from a NameError.
== Ruby D-Bus 0.5.0 - 2010-11-07
Features:
* Better binding of Ruby Exceptions to D-Bus Errors.
* Converted the package to a Gem (Issue#6).
* Converted the tutorial from Webgen to Markdown.
Bug fixes:
* Don't pass file descriptors to subprocesses.
* Fixed InterfaceElement::validate_name (Ticket#38, by Herwin Weststrate).
* Fixed a typo in InvalidDestinationName description (Ticket#40).
== Ruby D-Bus 0.4.0 - 2010-08-20
Features:
* TCP transport (by pangdudu)
* Enabled test code coverage report (rcov)
Bug fixes:
* Classes should not share all interfaces (Ticket#36/Issue#5)
* Ruby 1.9 compatibility (Ticket#37, by Myra Nelson)
== Ruby D-Bus 0.3.1 - 2010-07-22
Bug fixes:
* Many on_signal could cause DBus.Error.LimitsExceeded bnc#617350).
Don't add a match rule that already exists, enable removing match
rules. Now only one handler for a rule is called (but it is possible
for one signal to match more rules). This reverts the half-fix done
to fix Issue#3
* Re-added InterfaceElement#add_param for compatibility.
* Handle more ways which tell us that a bus connection has died.
== Ruby D-Bus 0.3.0 - 2010-03-28
Bug fixes:
* Fixed "undefined method `get_node' for nil:NilClass"
on Ubuntu Karmic (Ticket#34).
* Get the session bus address even if unset in ENV (Issue#4).
* Improved exceptions a bit:
UndefinedInterface, InvalidMethodName, NoMethodError, no RuntimeException
These are by Klaus Kaempf:
* Make the signal dispatcher call all handlers (Issue#3).
* Run on Ruby < 1.8.7 (Issue#2).
* Avoid needless DBus::IncompleteBufferException (Ticket#33).
* Don't ignore DBus Errors in request_service, raise them (Ticket#32).
Features:
* Automatic signature inference for variants.
* Introduced FormalParameter where a plain pair had been used.
== Ruby D-Bus 0.2.12 - 2010-01-24
Bug fixes:
* Fixed a long-standing bug where a service activated by the bus
would fail with "undefined method `get_node' for nil:NilClass"
(Tickets#25 and #29).
== Ruby D-Bus 0.2.11 - 2009-11-12
Features:
* Added DBus::Service#unexport (da1l6).
Bug fixes:
* Return org.freedesktop.DBus.Error.UnknownObject instead of crashing
(Ticket#31).
* Rescue exceptions in dbus_methods and reply with DBus errors instead of
crashing (da1l6).
* Better exception messages when sending nil, or mismatched structs.
* Call mktemp without --tmpdir, to build on older distros.
== Ruby D-Bus 0.2.10 - 2009-09-10
Bug fixes:
* DBus::Service.exists? fixed (Murat Demirten).
* Ruby 1.9 fixes (Jedediah Smith).
* Fixed an endless sleep in DBus::Main.run (bnc#537401).
* Added details to PacketMarshaller exceptions (bnc#538050).
(bnc#FOO refers to https://bugzilla.novell.com/show_bug.cgi?id=FOO )
== Ruby D-Bus "I'm not dead" 0.2.9 - 2009-08-26
Thank you to Paul and Arnaud for starting the project. I, Martin
Vidner, am continuing with it on GitHub.
* Fixed passing an array through a variant (no ticket).
* Fixed marshalling "av" (Ticket #30).
* Fixed variant alignment (Ticket #27).
* Added DBus::Main.quit.
* Mention the DBus interface in a NameError for an unknown method.
* Fixed ruby-1.9 "warning: default `to_a' will be obsolete".
* Added Rakefile and gemspec.
== Ruby D-Bus "Thanks for all the fish" 0.2.1 - 2007-12-29
More bugfixes, mostly supplied by users supplying us with patches. Thanks!
* Support for new types added:
- dict (courtesy of Drake Wilson);
- double (courtesy of Patrick Sissons);
- variant.
* Improved exception raise support (courtesy of Sjoerd Simons,
Patrick Sissons).
* Some polish (removed debug output, solved unnecessary warnings).
* Documentation updates, example fixes and updates.
== Ruby D-Bus "Almost live from DebConf 7" 0.2.0 - 2007-06-02
Again a bugfix release, also meant to be the public release
for exploratory purposes. New in 0.2.0:
* Complete tutorial revamp.
* Relicensed to the LGPL.
== Ruby D-Bus "Release Often" 0.1.1 - 2007-04-23
Bugfix release. Fixes hardcoded string for requesting bus names,
found by Rudi Cilibrasi.
== Ruby D-Bus "Happy Birthday Paul" 0.1.0 - 2007-04-17
First release. Supports most of D-Bus' features.