ruby-dbus-0.11.0/ 0000755 0000041 0000041 00000000000 12301727224 013535 5 ustar www-data www-data ruby-dbus-0.11.0/Rakefile 0000644 0000041 0000041 00000002672 12301727223 015210 0 ustar www-data www-data #! /usr/bin/env ruby
require 'rake'
require 'fileutils'
include FileUtils
require 'tmpdir'
require 'rspec/core/rake_task'
require "packaging"
Packaging.configuration do |conf|
conf.obs_project = "devel:languages:ruby:extensions"
conf.package_name = "rubygem-ruby-dbus"
conf.obs_sr_project = "openSUSE:Factory"
conf.skip_license_check << /^[^\/]*$/
conf.skip_license_check << /^(doc|examples|test)\/.*/
# "Ruby on Rails is released under the MIT License."
# but the files are missing copyright headers
conf.skip_license_check << /^lib\/dbus\/core_ext\//
end
desc 'Default: run specs in the proper environment'
task :default => :spec
task :test => :spec
RSpec::Core::RakeTask.new("bare:spec") do |t|
t.pattern = "**/test/**/*_spec.rb"
t.rspec_opts = "--color --format doc"
end
%w(spec).each do |tname|
desc "Run bare:#{tname} in the proper environment"
task tname do |t|
cd "test/tools" do
sh "./test_env rake bare:#{tname}"
end
end
end
if ENV["TRAVIS"]
require "coveralls/rake/task"
Coveralls::RakeTask.new
task :default => "coveralls:push"
end
#remove tarball implementation and create gem for this gemfile
Rake::Task[:tarball].clear
desc "Build a package from a clone of the local Git repo"
task :tarball do |t|
Dir.mktmpdir do |temp|
sh "git clone . #{temp}"
cd temp do
sh "gem build ruby-dbus.gemspec"
end
sh "rm -f package/*.gem"
cp Dir.glob("#{temp}/*.gem"), "package"
end
end
ruby-dbus-0.11.0/examples/ 0000755 0000041 0000041 00000000000 12301727223 015352 5 ustar www-data www-data ruby-dbus-0.11.0/examples/rhythmbox/ 0000755 0000041 0000041 00000000000 12301727223 017376 5 ustar www-data www-data ruby-dbus-0.11.0/examples/rhythmbox/playpause.rb 0000755 0000041 0000041 00000001006 12301727223 021726 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.11.0/examples/gdbus/ 0000755 0000041 0000041 00000000000 12301727223 016456 5 ustar www-data www-data ruby-dbus-0.11.0/examples/gdbus/gdbus.glade 0000644 0000041 0000041 00000016444 12301727223 020571 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.11.0/examples/gdbus/gdbus 0000755 0000041 0000041 00000016414 12301727223 017516 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.11.0/examples/gdbus/launch.sh 0000755 0000041 0000041 00000000101 12301727223 020257 0 ustar www-data www-data #!/bin/sh
set -e
# for the lazy typer
ruby -w -I ../../lib gdbus
ruby-dbus-0.11.0/examples/no-introspect/ 0000755 0000041 0000041 00000000000 12301727223 020156 5 ustar www-data www-data ruby-dbus-0.11.0/examples/no-introspect/nm-test.rb 0000755 0000041 0000041 00000001077 12301727223 022102 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.11.0/examples/no-introspect/tracker-test.rb 0000755 0000041 0000041 00000001161 12301727223 023115 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.11.0/examples/service/ 0000755 0000041 0000041 00000000000 12301727223 017012 5 ustar www-data www-data ruby-dbus-0.11.0/examples/service/service_newapi.rb 0000755 0000041 0000041 00000002204 12301727223 022343 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.11.0/examples/service/call_service.rb 0000755 0000041 0000041 00000001115 12301727223 021773 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.11.0/examples/simple/ 0000755 0000041 0000041 00000000000 12301727223 016643 5 ustar www-data www-data ruby-dbus-0.11.0/examples/simple/call_introspect.rb 0000755 0000041 0000041 00000001440 12301727223 022357 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.11.0/examples/simple/properties.rb 0000755 0000041 0000041 00000001007 12301727223 021365 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.11.0/examples/utils/ 0000755 0000041 0000041 00000000000 12301727223 016512 5 ustar www-data www-data ruby-dbus-0.11.0/examples/utils/listnames.rb 0000755 0000041 0000041 00000000265 12301727223 021044 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.11.0/examples/utils/notify.rb 0000755 0000041 0000041 00000000572 12301727223 020356 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.11.0/doc/ 0000755 0000041 0000041 00000000000 12301727223 014301 5 ustar www-data www-data ruby-dbus-0.11.0/doc/Tutorial.md 0000644 0000041 0000041 00000041600 12301727223 016427 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.11.0/doc/ex-signal.rb 0000755 0000041 0000041 00000000115 12301727223 016515 0 ustar www-data www-data #! /usr/bin/env ruby
require 'example-helper.rb'
example 'ex-signal.body.rb'
ruby-dbus-0.11.0/doc/ex-signal.body.rb 0000644 0000041 0000041 00000001102 12301727223 017443 0 ustar www-data www-data sysbus = DBus.system_bus
login_s = sysbus['org.freedesktop.login1'] # part of systemd
login_o = login_s.object '/org/freedesktop/login1'
login_o.introspect
login_o.default_iface = 'org.freedesktop.login1.Manager'
# to trigger this signal, login on the Linux console
login_o.on_signal("SessionNew") do |name, opath|
puts "New session: #{name}"
session_o = login_s.object(opath)
session_o.introspect
session_i = session_o['org.freedesktop.login1.Session']
uid, user_opath = session_i['User']
puts "Its UID: #{uid}"
end
main = DBus::Main.new
main << sysbus
main.run
ruby-dbus-0.11.0/doc/ex-properties.rb 0000755 0000041 0000041 00000000121 12301727223 017431 0 ustar www-data www-data #! /usr/bin/env ruby
require 'example-helper.rb'
example 'ex-properties.body.rb'
ruby-dbus-0.11.0/doc/ex-calling-methods.rb 0000755 0000041 0000041 00000000126 12301727223 020314 0 ustar www-data www-data #! /usr/bin/env ruby
require 'example-helper.rb'
example 'ex-calling-methods.body.rb'
ruby-dbus-0.11.0/doc/Reference.md 0000644 0000041 0000041 00000013773 12301727223 016534 0 ustar www-data www-data Ruby D-Bus Reference
====================
This is a reference-style documentation. It's not [a tutorial for
beginners](http://dbus.freedesktop.org/doc/dbus-tutorial.html), the
reader should have knowledge of basic DBus concepts.
Client Side
-----------
This section should be enough if you only want to consume DBus APIs.
### Basic Concepts
#### Setting Up
The following code is assumed as a prolog to all following ones
{include:file:doc/ex-setup.rb}
#### Calling Methods
1. {DBus.session_bus Connect to the session bus};
{DBus::Connection#[] get the screensaver service}
{DBus::Service#object and its screensaver object}.
2. Perform {DBus::ProxyObject#introspect explicit introspection}
to define the interfaces and methods
on the {DBus::ProxyObject object proxy}
([I#28](https://github.com/mvidner/ruby-dbus/issues/28)).
3. Call one of its methods in a loop, solving [xkcd#196](http://xkcd.com/196).
{include:file:doc/ex-calling-methods.body.rb}
##### Retrieving Return Values
A method proxy always returns an array of values. This is to
accomodate the rare cases of a DBus method specifying more than one
*out* parameter. For nearly all methods you should use `Method[0]` or
`Method.first`
([I#30](https://github.com/mvidner/ruby-dbus/issues/30)).
# wrong
if upower_i.SuspendAllowed # [false] is true!
upower_i.Suspend
end
# right
if upower_i.SuspendAllowed[0]
upower_i.Suspend
end
#### Accessing Properties
To access properties, think of the {DBus::ProxyObjectInterface interface} as a
{DBus::ProxyObjectInterface#[] hash} keyed by strings,
or use {DBus::ProxyObjectInterface#all_properties} to get
an actual Hash of them.
{include:file:doc/ex-properties.body.rb}
(TODO a writable property example)
Note that unlike for methods where the interface is inferred if unambiguous,
for properties the interface must be explicitly chosen.
That is because {DBus::ProxyObject} uses the {DBus::ProxyObject Hash#[]} API
to provide the {DBus::ProxyObjectInterface interfaces}, not the properties.
#### Asynchronous Operation
If a method call has a block attached, it is asynchronous and the block
is invoked on receiving a method_return message or an error message
##### Main Loop
For asynchronous operation an event loop is necessary. Use {DBus::Main}:
# [set up signal handlers...]
main = DBus::Main.new
main << mybus
main.run
Alternately, run the GLib main loop and add your DBus connections to it via
{DBus::Connection#glibize}.
#### Receiving Signals
To receive signals for a specific object and interface, use
{DBus::ProxyObjectInterface#on\_signal}(name, &block) or
{DBus::ProxyObject#on_signal}(name, &block), for the default interface.
{include:file:doc/ex-signal.body.rb}
### Intermediate Concepts
#### Names
#### Types and Values, D-Bus -> Ruby
D-Bus booleans, numbers, strings, arrays and dictionaries become their straightforward Ruby counterparts.
Structs become arrays.
Object paths become strings.
Variants are simply unpacked to become their contained type.
(ISSUE: prevents proper round-tripping!)
#### Types and Values, Ruby -> D-Bus
D-Bus has stricter typing than Ruby, so the library must decide
which D-Bus type to choose. Most of the time the choice is dictated
by the D-Bus signature.
##### Variants
If the signature expects a Variant
(which is the case for all Properties!) then an explicit mechanism is needed.
1. A pair [{DBus::Type::Type}, value] specifies to marshall *value* as
that specified type.
The pair can be produced by {DBus.variant}(signature, value) which
gives the same result as [{DBus.type}(signature), value].
ISSUE: using something else than cryptic signatures is even more painful
than remembering the signatures!
foo_i['Bar'] = DBus.variant("au", [0, 1, 1, 2, 3, 5, 8])
2. Other values are tried to fit one of these:
Boolean, Double, Array of Variants, Hash of String keyed Variants,
String, Int32, Int64.
3. **Deprecated:** A pair [String, value], where String is a valid
signature of a single complete type, marshalls value as that
type. This will hit you when you rely on method (2) but happen to have
a particular string value in an array.
##### Byte Arrays
If a byte array (`ay`) is expected you can pass a String too.
The bytes sent are according to the string's
[encoding](http://ruby-doc.org/core-1.9.3/Encoding.html).
##### nil
`nil` is not allowed by D-Bus and attempting to send it raises an exception
(but see [I#16](https://github.com/mvidner/ruby-dbus/issues/16)).
#### Errors
D-Bus calls can reply with an error instead of a return value. An error is
translated to a Ruby exception, an instance of {DBus::Error}.
begin
network_manager.sleep
rescue DBus::Error => e
puts e unless e.name == "org.freedesktop.NetworkManager.AlreadyAsleepOrAwake"
end
#### Interfaces
Methods, properties and signals of a D-Bus object always belong to one of its interfaces.
Methods can be called without specifying their interface, as long as there is no ambiguity.
There are two ways to resolve ambiguities:
1. assign an interface name to {DBus::ProxyObject#default_iface}.
2. get a specific {DBus::ProxyObjectInterface interface} of the object,
with {DBus::ProxyObject#[]} and call methods from there.
Signals and properties only work with a specific interface.
#### Thread Safety
Not there. An [incomplete attempt](https://github.com/mvidner/ruby-dbus/tree/multithreading) was made.
### Advanced Concepts
#### Bus Addresses
#### Without Introspection
#### Name Overloading
Service Side
------------
When you want to provide a DBus API.
(check that client and service side have their counterparts)
### Basic
#### Exporting a Method
##### Interfaces
##### Methods
##### Bus Names
##### Errors
#### Exporting Properties
### Advanced
#### Inheritance
#### Names
Specification Conformance
-------------------------
This section lists the known deviations from version 0.19 of
[the specification][spec].
[spec]: http://dbus.freedesktop.org/doc/dbus-specification.html
1. Properties support is basic.
ruby-dbus-0.11.0/doc/example-helper.rb 0000644 0000041 0000041 00000000230 12301727223 017531 0 ustar www-data www-data # Fin
$:.unshift File.expand_path("../../lib", __FILE__)
load 'ex-setup.rb'
def example(filename)
eval(File.read(filename), binding, filename, 1)
end
ruby-dbus-0.11.0/doc/ex-calling-methods.body.rb 0000644 0000041 0000041 00000000276 12301727223 021253 0 ustar www-data www-data mybus = DBus.session_bus
service = mybus['org.freedesktop.ScreenSaver']
object = service.object '/ScreenSaver'
object.introspect
loop do
object.SimulateUserActivity
sleep 5 * 60
end
ruby-dbus-0.11.0/doc/ex-properties.body.rb 0000644 0000041 0000041 00000000426 12301727223 020372 0 ustar www-data www-data sysbus = DBus.system_bus
upower_s = sysbus['org.freedesktop.UPower']
upower_o = upower_s.object '/org/freedesktop/UPower'
upower_o.introspect
upower_i = upower_o['org.freedesktop.UPower']
on_battery = upower_i['OnBattery']
puts "Is the computer on battery now? #{on_battery}"
ruby-dbus-0.11.0/doc/ex-setup.rb 0000644 0000041 0000041 00000000404 12301727223 016376 0 ustar www-data www-data #! /usr/bin/env ruby
require 'rubygems' # Not needed since Ruby 1.9
require 'dbus' # The gem is 'ruby-dbus' but the require is 'dbus'
# Connect to a well-known address. Most apps need only one of them.
mybus = DBus.session_bus
sysbus = DBus.system_bus
ruby-dbus-0.11.0/ruby-dbus.gemspec 0000644 0000041 0000041 00000001376 12301727223 017024 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 = "Pure Ruby module for interaction with D-Bus IPC system"
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,examples,lib,test}/**/*", "COPYING", "NEWS", "Rakefile", "README.md", "ruby-dbus.gemspec", "VERSION"].to_a.sort
s.require_path = "lib"
s.required_ruby_version = ">= 1.9.3"
s.add_development_dependency("packaging_rake_tasks")
s.add_development_dependency("rspec")
end
ruby-dbus-0.11.0/NEWS 0000644 0000041 0000041 00000017162 12301727223 014242 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.11.0 - 2014-02-17
API:
* Connection: split off MessageQueue, marked other methods as private.
Requirements:
* converted tests to RSpec, rather mechanically for now
== Ruby D-Bus 0.10.0 - 2014-01-10
Bug fixes:
* fixed "Interfaces added with singleton_class.instance_eval aren't
exported" (Issue#22, by miaoufkirsh)
Requirements:
* Require ruby 1.9.3, stopped supporting 1.8.7.
== Ruby D-Bus 0.9.3 - 2014-01-02
Bug fixes:
* re-added COPYING, NEWS, README.md to the gem (Issue#47,
by Cédric Boutillier)
Packaging:
* use packaging_rake_tasks
== Ruby D-Bus 0.9.2 - 2013-05-08
Features:
* Ruby strings can be passed where byte arrays ("ay") are expected
(Issue#40, by Jesper B. Rosenkilde)
Bug fixes:
* Fixed accessing ModemManager properties (Issue#41, reported
by Ernest Bursa). MM introspection produces two elements
for a single interface; merge them.
== Ruby D-Bus 0.9.1 - 2013-04-23
Bug fixes:
* Prefer /etc/machine-id to /var/lib/dbus/machine-id
when DBUS_SESSION_BUS_ADDRESS is unset (Issue#39, by WU Jun).
== Ruby D-Bus 0.9.0 - 2012-11-06
Features:
* When calling methods, the interface can be left unspecified if unambiguous
(Damiano Stoffie)
* YARD documentation, Reference.md
Bug fixes:
* Introspection attribute "direction" can be omitted
as allowed by the specification (Noah Meyerhans).
* ProxyObjectInterface#on_signal no longer needs the "bus" parameter
(Issue#31, by Damiano Stoffie)
== Ruby D-Bus 0.8.0 - 2012-09-20
Features:
* Add Anonymous authentication (Issue#27, by Walter Brebels).
* Use Nokogiri for XML parsing when available (Issue#24, by Geoff Youngs).
Bug fixes:
* Use SCM_CREDS authentication only on FreeBSD, not on OpenBSD (Issue#21,
reported by Adde Nilsson).
* Recognize signature "h" (UNIX_FD) used eg. by Upstart (Issue#23,
by Bernd Ahlers).
* Find the session bus also via launchd, on OS X (Issue#20, reported
by Paul Sturgess).
Other:
* Now doing continuous integration with Travis:
http://travis-ci.org/#!/mvidner/ruby-dbus
== 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.
ruby-dbus-0.11.0/lib/ 0000755 0000041 0000041 00000000000 12301727223 014302 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus.rb 0000644 0000041 0000041 00000004502 12301727223 015565 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_relative "dbus/auth"
require_relative "dbus/bus"
require_relative "dbus/core_ext/class/attribute"
require_relative "dbus/error"
require_relative "dbus/export"
require_relative "dbus/introspect"
require_relative "dbus/logger"
require_relative "dbus/marshall"
require_relative "dbus/matchrule"
require_relative "dbus/message"
require_relative "dbus/message_queue"
require_relative "dbus/proxy_object"
require_relative "dbus/proxy_object_factory"
require_relative "dbus/proxy_object_interface"
require_relative "dbus/type"
require_relative "dbus/xml"
require "socket"
require "thread"
# = 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.11.0/lib/dbus/ 0000755 0000041 0000041 00000000000 12301727223 015237 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus/export.rb 0000644 0000041 0000041 00000010620 12301727223 017104 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. Hash: String => Interface
class_attribute :intfs
# 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
# 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
dbus_msg_exc = msg.annotate_exception(ex)
reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
end
@service.bus.message_queue.push(reply)
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.11.0/lib/dbus/core_ext/ 0000755 0000041 0000041 00000000000 12301727223 017047 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus/core_ext/module/ 0000755 0000041 0000041 00000000000 12301727223 020334 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus/core_ext/module/remove_method.rb 0000644 0000041 0000041 00000000711 12301727223 023515 0 ustar www-data www-data # copied from activesupport/core_ext from Rails, MIT license
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
class Module
def remove_possible_method(method)
if method_defined?(method) || private_method_defined?(method)
undef_method(method)
end
end
def redefine_method(method, &block)
remove_possible_method(method)
define_method(method, &block)
end
end
ruby-dbus-0.11.0/lib/dbus/core_ext/array/ 0000755 0000041 0000041 00000000000 12301727223 020165 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus/core_ext/array/extract_options.rb 0000644 0000041 0000041 00000001721 12301727223 023740 0 ustar www-data www-data # copied from activesupport/core_ext from Rails, MIT license
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
class Hash
# By default, only instances of Hash itself are extractable.
# Subclasses of Hash may implement this method and return
# true to declare themselves as extractable. If a Hash
# is extractable, Array#extract_options! pops it from
# the Array when it is the last element of the Array.
def extractable_options?
instance_of?(Hash)
end
end
class Array
# Extracts options from a set of arguments. Removes and returns the last
# element in the array if it's a hash, otherwise returns a blank hash.
#
# def options(*args)
# args.extract_options!
# end
#
# options(1, 2) # => {}
# options(1, 2, a: :b) # => {:a=>:b}
def extract_options!
if last.is_a?(Hash) && last.extractable_options?
pop
else
{}
end
end
end
ruby-dbus-0.11.0/lib/dbus/core_ext/kernel/ 0000755 0000041 0000041 00000000000 12301727223 020327 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus/core_ext/kernel/singleton_class.rb 0000644 0000041 0000041 00000000536 12301727223 024047 0 ustar www-data www-data # copied from activesupport/core_ext from Rails, MIT license
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
module Kernel
# class_eval on an object acts like singleton_class.class_eval.
def class_eval(*args, &block)
singleton_class.class_eval(*args, &block)
end
end
ruby-dbus-0.11.0/lib/dbus/core_ext/class/ 0000755 0000041 0000041 00000000000 12301727223 020154 5 ustar www-data www-data ruby-dbus-0.11.0/lib/dbus/core_ext/class/attribute.rb 0000644 0000041 0000041 00000010454 12301727223 022510 0 ustar www-data www-data # copied from activesupport/core_ext from Rails, MIT license
# https://github.com/rails/rails/tree/5aa869861c192daceafe3a3ee50eb93f5a2b7bd2/activesupport/lib/active_support/core_ext
require 'dbus/core_ext/kernel/singleton_class'
require 'dbus/core_ext/module/remove_method'
require 'dbus/core_ext/array/extract_options'
class Class
# Declare a class-level attribute whose value is inheritable by subclasses.
# Subclasses can change their own value and it will not impact parent class.
#
# class Base
# class_attribute :setting
# end
#
# class Subclass < Base
# end
#
# Base.setting = true
# Subclass.setting # => true
# Subclass.setting = false
# Subclass.setting # => false
# Base.setting # => true
#
# In the above case as long as Subclass does not assign a value to setting
# by performing Subclass.setting = _something_ , Subclass.setting
# would read value assigned to parent class. Once Subclass assigns a value then
# the value assigned by Subclass would be returned.
#
# This matches normal Ruby method inheritance: think of writing an attribute
# on a subclass as overriding the reader method. However, you need to be aware
# when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
# In such cases, you don't want to do changes in places but use setters:
#
# Base.setting = []
# Base.setting # => []
# Subclass.setting # => []
#
# # Appending in child changes both parent and child because it is the same object:
# Subclass.setting << :foo
# Base.setting # => [:foo]
# Subclass.setting # => [:foo]
#
# # Use setters to not propagate changes:
# Base.setting = []
# Subclass.setting += [:foo]
# Base.setting # => []
# Subclass.setting # => [:foo]
#
# For convenience, an instance predicate method is defined as well.
# To skip it, pass instance_predicate: false.
#
# Subclass.setting? # => false
#
# Instances may overwrite the class value in the same way:
#
# Base.setting = true
# object = Base.new
# object.setting # => true
# object.setting = false
# object.setting # => false
# Base.setting # => true
#
# To opt out of the instance reader method, pass instance_reader: false.
#
# object.setting # => NoMethodError
# object.setting? # => NoMethodError
#
# To opt out of the instance writer method, pass instance_writer: false.
#
# object.setting = false # => NoMethodError
#
# To opt out of both instance methods, pass instance_accessor: false.
def class_attribute(*attrs)
options = attrs.extract_options!
instance_reader = options.fetch(:instance_accessor, true) && options.fetch(:instance_reader, true)
instance_writer = options.fetch(:instance_accessor, true) && options.fetch(:instance_writer, true)
instance_predicate = options.fetch(:instance_predicate, true)
attrs.each do |name|
define_singleton_method(name) { nil }
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
ivar = "@#{name}"
define_singleton_method("#{name}=") do |val|
singleton_class.class_eval do
remove_possible_method(name)
define_method(name) { val }
end
if singleton_class?
class_eval do
remove_possible_method(name)
define_method(name) do
if instance_variable_defined? ivar
instance_variable_get ivar
else
singleton_class.send name
end
end
end
end
val
end
if instance_reader
remove_possible_method name
define_method(name) do
if instance_variable_defined?(ivar)
instance_variable_get ivar
else
self.class.public_send name
end
end
define_method("#{name}?") { !!public_send(name) } if instance_predicate
end
attr_writer name if instance_writer
end
end
private
unless respond_to?(:singleton_class?)
def singleton_class?
ancestors.first != self
end
end
end
ruby-dbus-0.11.0/lib/dbus/proxy_object.rb 0000644 0000041 0000041 00000012112 12301727223 020270 0 ustar www-data www-data # This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
# Copyright (C) 2009-2014 Martin Vidner
#
# 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 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
# @return [String] The name of the default interface of the object.
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
# @param [String] intfname
# @return [ProxyObjectInterface]
def [](intfname)
@interfaces[intfname]
end
# Maps the given interface name _intfname_ to the given interface _intf.
# @param [String] intfname
# @param [ProxyObjectInterface] intf
# @return [ProxyObjectInterface]
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)
define_shortcut_methods()
xml
end
# For each non duplicated method name in any interface present on the
# caller, defines a shortcut method dynamically.
# This function is automatically called when a {ProxyObject} is
# introspected.
def define_shortcut_methods
# builds a list of duplicated methods
dup_meths, univocal_meths = [],{}
@interfaces.each_value do |intf|
intf.methods.each_value do |meth|
name = meth.name.to_sym
# don't overwrite instance methods!
if dup_meths.include? name or self.class.instance_methods.include? name
next
elsif univocal_meths.include? name
univocal_meths.delete name
dup_meths << name
else
univocal_meths[name] = intf
end
end
end
univocal_meths.each do |name, intf|
# creates a shortcut function that forwards each call to the method on
# the appropriate intf
singleton_class.class_eval do
define_method name do |*args, &reply_handler|
intf.method(name).call(*args, &reply_handler)
end
end
end
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.
# @return [void]
def on_signal(name, &block)
if @default_iface and has_iface?(@default_iface)
@interfaces[@default_iface].on_signal(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
# Returns the singleton class of the object.
def singleton_class
(class << self ; self ; end)
end
end # class ProxyObject
end
ruby-dbus-0.11.0/lib/dbus/bus.rb 0000644 0000041 0000041 00000053776 12301727223 016377 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 caan 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'
# = 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 at the given _path_.
# @return [ProxyObject]
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?
DBus.logger.debug "Warning, unknown object #{path}"
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
# pop and push messages here
attr_reader :message_queue
# 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)
@message_queue = MessageQueue.new(path)
@unique_name = nil
@method_call_replies = Hash.new
@method_call_msgs = Hash.new
@signal_matchrules = Hash.new
@proxy = nil
@object_root = Node.new("/")
end
# Dispatch all messages that are available in the queue,
# but do not block on the queue.
# Called by a main loop when something is available in the queue
def dispatch_message_queue
while (msg = @message_queue.pop(:non_block)) # FIXME EOFError
process(msg)
end
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(@message_queue.socket.fileno)
@channels << gio
gio.add_watch(GLib::IOChannel::IN) do |c, ch|
dispatch_message_queue
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: "
# @api private
# 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
@message_queue.push(message)
end
ret
end
# @api private
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
# @api private
# 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.
# @return [Service]
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
# @api private
# Wait for a message to arrive. Return it once it is available.
def wait_for_message
@message_queue.pop # FIXME EOFError
end
# @api private
# 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
@message_queue.push(m)
@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
# @api private
# 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
DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}"
# don't ask for the same match if we override it
unless @signal_matchrules.key?(mrs)
DBus.logger.debug "Asked for a new match"
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
# @api private
# 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
DBus.logger.debug "no return code for mcs: #{mcs.inspect} m: #{m.inspect}"
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"
DBus.logger.debug "Got method call on /org/freedesktop/DBus"
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")
@message_queue.push(reply)
# 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)
@message_queue.push(reply)
else
obj = node.object
return if obj.nil? # FIXME, pushes 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
DBus.logger.debug "Unknown message type: #{m.message_type}"
end
rescue Exception => ex
raise m.annotate_exception(ex)
end
# Retrieves the Service with the given _name_.
# @return [Service]
def service(name)
# The service might not exist at this time so we cannot really check
# anything
Service.new(name, self)
end
alias :[] :service
# @api private
# 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
@message_queue.push(m)
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
DBus.logger.debug "Got hello reply. Our unique_name is #{@unique_name}"
end
@service = Service.new(@unique_name, self)
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 || "launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET")
send_hello
end
def address_from_file
# systemd uses /etc/machine-id
# traditional dbus uses /var/lib/dbus/machine-id
machine_id_path = Dir['{/etc,/var/lib/dbus}/machine-id'].first
return nil unless machine_id_path
machine_id = File.read(machine_id_path).chomp
display = ENV["DISPLAY"][/:(\d+)\.?/, 1]
bus_file_path = File.join(ENV["HOME"], "/.dbus/session-bus/#{machine_id}-#{display}")
return nil unless File.exists?(bus_file_path)
File.open(bus_file_path).each_line 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)
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)
send_hello
end
end
# See ASystemBus
class SystemBus < ASystemBus
include Singleton
end
# Shortcut for the {SystemBus} instance
# @return [Connection]
def DBus.system_bus
SystemBus.instance
end
# Shortcut for the {SessionBus} instance
# @return [Connection]
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.message_queue.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.message_queue.message_from_buffer_nonblock
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.message_queue.buffer_from_socket_nonblock
rescue EOFError, SystemCallError
@buses.delete socket # this bus died
next
end
while m = b.message_queue.message_from_buffer_nonblock
b.process(m)
end
end
end
end
end # class Main
end # module DBus
ruby-dbus-0.11.0/lib/dbus/marshall.rb 0000644 0000041 0000041 00000031542 12301727223 017374 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
# 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, Type::UNIX_FD
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')
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 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, Type::UNIX_FD
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 string is recieved and ay is expected, explode the string
if val.kind_of?(String) && type.child.sigtype == Type::BYTE
val = val.bytes
end
if not val.kind_of?(Enumerable)
raise TypeException, "Expected an Enumerable of #{type.child.inspect} 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.11.0/lib/dbus/xml.rb 0000644 0000041 0000041 00000010452 12301727223 016366 0 ustar www-data www-data # dbus/xml.rb - introspection parser, rexml/nokogiri abstraction
#
# This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
# Copyright (C) 2012 Geoff Youngs
#
# 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'
begin
require 'nokogiri'
rescue LoadError
end
module DBus
# = 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
class << self
attr_accessor :backend
end
# Creates a new parser for XML data in string _xml_.
def initialize(xml)
@xml = xml
end
class AbstractXML
def self.have_nokogiri?
Object.const_defined?('Nokogiri')
end
class Node
def initialize(node)
@node = node
end
# required methods
# returns node attribute value
def [](key)
end
# yields child nodes which match xpath of type AbstractXML::Node
def each(xpath)
end
end
# required methods
# initialize parser with xml string
def initialize(xml)
end
# yields nodes which match xpath of type AbstractXML::Node
def each(xpath)
end
end
class NokogiriParser < AbstractXML
class NokogiriNode < AbstractXML::Node
def [](key)
@node[key]
end
def each(path, &block)
@node.search(path).each { |node| block.call NokogiriNode.new(node) }
end
end
def initialize(xml)
@doc = Nokogiri.XML(xml)
end
def each(path, &block)
@doc.search("//#{path}").each { |node| block.call NokogiriNode.new(node) }
end
end
class REXMLParser < AbstractXML
class REXMLNode < AbstractXML::Node
def [](key)
@node.attributes[key]
end
def each(path, &block)
@node.elements.each(path) { |node| block.call REXMLNode.new(node) }
end
end
def initialize(xml)
@doc = REXML::Document.new(xml)
end
def each(path, &block)
@doc.elements.each(path) { |node| block.call REXMLNode.new(node) }
end
end
if AbstractXML.have_nokogiri?
@backend = NokogiriParser
else
@backend = REXMLParser
end
# return a pair: [list of Interfaces, list of direct subnode names]
def parse
# Using a Hash instead of a list helps merge split-up interfaces,
# a quirk observed in ModemManager (I#41).
interfaces = Hash.new do |hash, missing_key|
hash[missing_key] = Interface.new(missing_key)
end
subnodes = []
t = Time.now
d = IntrospectXMLParser.backend.new(@xml)
d.each("node/node") do |e|
subnodes << e["name"]
end
d.each("node/interface") do |e|
i = interfaces[e["name"]]
e.each("method") do |me|
m = Method.new(me["name"])
parse_methsig(me, m)
i << m
end
e.each("signal") do |se|
s = Signal.new(se["name"])
parse_methsig(se, s)
i << s
end
end
d = Time.now - t
if d > 2
DBus.logger.debug "Some XML took more that two secs to parse. Optimize me!"
end
[interfaces.values, subnodes]
end
######################################################################
private
# Parses a method signature XML element _e_ and initialises
# method/signal _m_.
def parse_methsig(e, m)
e.each("arg") do |ae|
name = ae["name"]
dir = ae["direction"]
sig = ae["type"]
if m.is_a?(DBus::Signal)
# Direction can only be "out", ignore it
m.add_fparam(name, sig)
elsif m.is_a?(DBus::Method)
case dir
# This is a method, so dir defaults to "in"
when "in", nil
m.add_fparam(name, sig)
when "out"
m.add_return(name, sig)
end
else
raise NotImplementedError, dir
end
end
end
end # class IntrospectXMLParser
end # module DBus
ruby-dbus-0.11.0/lib/dbus/message_queue.rb 0000644 0000041 0000041 00000011410 12301727223 020411 0 ustar www-data www-data # This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
# Copyright (C) 2009-2014 Martin Vidner
#
# 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 "fcntl"
require "socket"
module DBus
class MessageQueue
# The socket that is used to connect with the bus.
attr_reader :socket
def initialize(address)
@address = address
@buffer = ""
@is_tcp = false
connect
end
# TODO failure modes
#
# If _non_block_ is true, return nil instead of waiting
# EOFError may be raised
def pop(non_block = false)
buffer_from_socket_nonblock
message = message_from_buffer_nonblock
unless non_block
# we can block
while message.nil?
r, d, d = IO.select([@socket])
if r and r[0] == @socket
buffer_from_socket_nonblock
message = message_from_buffer_nonblock
end
end
end
message
end
def push(message)
@socket.write(message.marshall)
end
alias :<< :push
private
# Connect to the bus and initialize the connection.
def connect
addresses = @address.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
when "launchd"
connect_to_launchd 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 Exception => e
puts "Oops:", e
puts "Error: Could not establish connection to: #{@path}, will now exit."
exit(1) #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
def connect_to_launchd(params)
socket_var = params['env']
socket = `launchctl getenv #{socket_var}`.chomp
connect_to_unix 'path' => socket
end
# Initialize the connection to the bus.
def init_connection
@client = Client.new(@socket)
@client.authenticate
end
public # FIXME: fix Main loop instead
# Get and remove one message from the buffer.
# Return the message or nil.
def message_from_buffer_nonblock
return nil if @buffer.empty?
ret = nil
begin
ret, size = Message.new.unmarshall_buffer(@buffer)
@buffer.slice!(0, size)
rescue IncompleteBufferException
# fall through, let ret be null
end
ret
end
# The buffer size for messages.
MSG_BUF_SIZE = 4096
# Fill (append) the buffer from data that might be available on the
# socket.
# EOFError may be raised
def buffer_from_socket_nonblock
@buffer += @socket.read_nonblock(MSG_BUF_SIZE)
rescue EOFError
raise # the caller expects it
rescue Errno::EAGAIN
# fine, would block
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
end
end
ruby-dbus-0.11.0/lib/dbus/proxy_object_factory.rb 0000644 0000041 0000041 00000002743 12301727223 022030 0 ustar www-data www-data # This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
# Copyright (C) 2009-2014 Martin Vidner
#
# 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 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
ruby-dbus-0.11.0/lib/dbus/logger.rb 0000644 0000041 0000041 00000001477 12301727223 017054 0 ustar www-data www-data # dbus/logger.rb - debug logging
#
# This file is part of the ruby-dbus project
# Copyright (C) 2012 Martin Vidner
#
# 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 'logger'
module DBus
# Get the logger for the DBus module.
# The default one logs to STDERR,
# with DEBUG if $DEBUG is set, otherwise INFO.
def logger
unless defined? @logger
@logger = Logger.new(STDERR)
@logger.level = $DEBUG ? Logger::DEBUG : Logger::INFO
end
@logger
end
module_function :logger
# Set the logger for the DBus module
def logger=(logger)
@logger = logger
end
module_function :logger=
end
ruby-dbus-0.11.0/lib/dbus/proxy_object_interface.rb 0000644 0000041 0000041 00000007612 12301727223 022321 0 ustar www-data www-data # This file is part of the ruby-dbus project
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
# Copyright (C) 2009-2014 Martin Vidner
#
# 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 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
# Defines a method on the interface from the Method descriptor _m_.
def define_method_from_descriptor(m)
m.params.each do |fpar|
par = fpar.type
# This is the signature validity check
Type::Parser.new(par).parse
end
singleton_class.class_eval do
define_method m.name do |*args, &reply_handler|
if m.params.size != args.size
raise ArgumentError, "wrong number of arguments (#{args.size} for #{m.params.size})"
end
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
m.params.each do |fpar|
par = fpar.type
msg.add_param(par, args.shift)
end
@object.bus.send_sync_or_async(msg, &reply_handler)
end
end
@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
# @overload on_signal(name, &block)
# @overload on_signal(bus, name, &block)
# Registers a handler (code block) for a signal with _name_ arriving
# over the given _bus_. If no block is given, the signal is unregistered.
# Note that specifying _bus_ is discouraged and the option is kept only for
# backward compatibility.
# @return [void]
def on_signal(bus = @object.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.
# @return [Hash{String}]
def all_properties
self.object[PROPERTY_INTERFACE].GetAll(self.name)[0]
end
end # class ProxyObjectInterface
end
ruby-dbus-0.11.0/lib/dbus/auth.rb 0000644 0000041 0000041 00000020153 12301727223 016526 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.
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
# = Anonymous authentication class
class Anonymous < Authenticator
def authenticate
'527562792044427573' # Hex encoded version of "Ruby DBus"
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)
DBus.logger.debug "path: #{path.inspect}"
File.foreach(path) do |line|
if line.index(id) == 0
# Right line of file, read cookie
cookie = line.split(' ')[2].chomp
DBus.logger.debug "cookie: #{cookie.inspect}"
# 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,Anonymous]
end
# Start the authentication process.
def authenticate
if (RbConfig::CONFIG["target_os"] =~ /freebsd/)
@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]
DBus.logger.debug "auth_msg: #{auth_msg.inspect}"
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(" ")
DBus.logger.debug "readline: #{readline.inspect}"
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
DBus.logger.debug ":Starting msg: #{msg[0].inspect}"
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
DBus.logger.debug "state: #{@state}"
case @state
when :WaitingForData
DBus.logger.debug ":WaitingForData msg: #{msg[0].inspect}"
case msg[0]
when "DATA"
chall = msg[1]
resp, chall = @authenticator.data(chall)
DBus.logger.debug ":WaitingForData/DATA resp: #{resp.inspect}"
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
DBus.logger.debug ":WaitingForOk msg: #{msg[0].inspect}"
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
DBus.logger.debug ":WaitingForReject msg: #{msg[0].inspect}"
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.11.0/lib/dbus/introspect.rb 0000644 0000041 0000041 00000014402 12301727223 017757 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.
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
end # module DBus
ruby-dbus-0.11.0/lib/dbus/message.rb 0000644 0000041 0000041 00000020516 12301727223 017214 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
def to_s
"#{message_type} sender=#{sender} -> dest=#{destination} serial=#{serial} reply_serial=#{reply_serial} path=#{path}; interface=#{interface}; member=#{member} error_name=#{error_name}"
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)
headers = []
headers << [PATH, ["o", @path]] if @path
headers << [INTERFACE, ["s", @interface]] if @interface
headers << [MEMBER, ["s", @member]] if @member
headers << [ERROR_NAME, ["s", @error_name]] if @error_name
headers << [REPLY_SERIAL, ["u", @reply_serial]] if @reply_serial
headers << [DESTINATION, ["s", @destination]] if @destination
# SENDER is not sent, the message bus fills it in instead
headers << [SIGNATURE, ["g", @signature]] if @signature != ""
marshaller.append("a(yv)", headers)
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)
_, @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, _ = unmarshall_buffer(buf)
ret
end
# Make a new exception from ex, mark it as being caused by this message
# @api private
def annotate_exception(ex)
new_ex = ex.exception("#{ex}; caused by #{self}")
new_ex.set_backtrace(ex.backtrace)
new_ex
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.11.0/lib/dbus/matchrule.rb 0000644 0000041 0000041 00000006153 12301727223 017555 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.11.0/lib/dbus/type.rb 0000644 0000041 0000041 00000011561 12301727223 016551 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
# Mapping from type number to name and alignment.
TypeMapping = {
0 => ["INVALID", nil],
?y => ["BYTE", 1],
?b => ["BOOLEAN", 4],
?n => ["INT16", 2],
?q => ["UINT16", 2],
?i => ["INT32", 4],
?u => ["UINT32", 4],
?x => ["INT64", 8],
?t => ["UINT64", 8],
?d => ["DOUBLE", 8],
?r => ["STRUCT", 8],
?a => ["ARRAY", 4],
?v => ["VARIANT", 1],
?o => ["OBJECT_PATH", 4],
?s => ["STRING", 4],
?g => ["SIGNATURE", 1],
?e => ["DICT_ENTRY", 8],
?h => ["UNIX_FD", 4],
}
# Defines the set of constants
TypeMapping.each_pair do |key, value|
Type.const_set(value.first, key)
end
# 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 TypeMapping.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
TypeMapping[@sigtype].last
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 TypeMapping.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 = TypeMapping[@sigtype].first
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)
c = nextchar
raise SignatureException, "Parse error in #{@signature}" if c == nil
child = parse_one(c)
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.11.0/lib/dbus/error.rb 0000644 0000041 0000041 00000003016 12301727223 016715 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
# @example raise a generic error
# raise DBus.error, "message"
# @example raise a specific error
# 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.11.0/metadata.yml 0000644 0000041 0000041 00000007027 12301727224 016046 0 ustar www-data www-data --- !ruby/object:Gem::Specification
name: ruby-dbus
version: !ruby/object:Gem::Version
version: 0.11.0
platform: ruby
authors:
- Ruby DBus Team
autorequire:
bindir: bin
cert_chain: []
date: 2014-02-17 00:00:00.000000000 Z
dependencies:
- !ruby/object:Gem::Dependency
name: packaging_rake_tasks
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
- !ruby/object:Gem::Dependency
name: rspec
requirement: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
type: :development
prerelease: false
version_requirements: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
description: Pure Ruby module for interaction with D-Bus IPC system
email: ruby-dbus-devel@lists.luon.net
executables: []
extensions: []
extra_rdoc_files: []
files:
- COPYING
- NEWS
- README.md
- Rakefile
- VERSION
- doc/Reference.md
- doc/Tutorial.md
- doc/ex-calling-methods.body.rb
- doc/ex-calling-methods.rb
- doc/ex-properties.body.rb
- doc/ex-properties.rb
- doc/ex-setup.rb
- doc/ex-signal.body.rb
- doc/ex-signal.rb
- doc/example-helper.rb
- 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/core_ext/array/extract_options.rb
- lib/dbus/core_ext/class/attribute.rb
- lib/dbus/core_ext/kernel/singleton_class.rb
- lib/dbus/core_ext/module/remove_method.rb
- lib/dbus/error.rb
- lib/dbus/export.rb
- lib/dbus/introspect.rb
- lib/dbus/logger.rb
- lib/dbus/marshall.rb
- lib/dbus/matchrule.rb
- lib/dbus/message.rb
- lib/dbus/message_queue.rb
- lib/dbus/proxy_object.rb
- lib/dbus/proxy_object_factory.rb
- lib/dbus/proxy_object_interface.rb
- lib/dbus/type.rb
- lib/dbus/xml.rb
- ruby-dbus.gemspec
- test/async_spec.rb
- test/binding_spec.rb
- test/bus_and_xml_backend_spec.rb
- test/bus_driver_spec.rb
- test/bus_spec.rb
- test/byte_array_spec.rb
- test/err_msg_spec.rb
- test/introspect_xml_parser_spec.rb
- test/introspection_spec.rb
- test/main_loop_spec.rb
- test/property_spec.rb
- test/server_robustness_spec.rb
- test/server_spec.rb
- test/service_newapi.rb
- test/session_bus_spec_manual.rb
- test/signal_spec.rb
- test/spec_helper.rb
- test/thread_safety_spec.rb
- test/tools/dbus-launch-simple
- test/tools/dbus-limited-session.conf
- test/tools/test_env
- test/tools/test_server
- test/type_spec.rb
- test/value_spec.rb
- test/variant_spec.rb
homepage: https://trac.luon.net/ruby-dbus
licenses:
- LGPL v2.1
metadata: {}
post_install_message:
rdoc_options: []
require_paths:
- lib
required_ruby_version: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: 1.9.3
required_rubygems_version: !ruby/object:Gem::Requirement
requirements:
- - '>='
- !ruby/object:Gem::Version
version: '0'
requirements: []
rubyforge_project:
rubygems_version: 2.0.3
signing_key:
specification_version: 4
summary: Ruby module for interaction with D-Bus
test_files: []
has_rdoc:
ruby-dbus-0.11.0/test/ 0000755 0000041 0000041 00000000000 12301727223 014513 5 ustar www-data www-data ruby-dbus-0.11.0/test/bus_spec.rb 0000755 0000041 0000041 00000001014 12301727223 016642 0 ustar www-data www-data #!/usr/bin/env rspec
# Test the bus class
require_relative "spec_helper"
require "dbus"
describe "BusTest" do
before(:each) do
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
@svc.object("/").introspect
end
it "tests introspection not leaking" do
# peek inside the object to see if a cleanup step worked or not
some_hash = @bus.instance_eval { @method_call_replies || Hash.new }
# fail: "there are leftover method handlers"
expect(some_hash.size).to eq(0)
end
end
ruby-dbus-0.11.0/test/value_spec.rb 0000755 0000041 0000041 00000005321 12301727223 017172 0 ustar www-data www-data #!/usr/bin/env rspec
# -*- coding: utf-8 -*-
require_relative "spec_helper"
require "dbus"
describe "ValueTest" do
before(:each) do
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
it "tests passing an array of structs through a variant" do
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)
end
it "tests passing an array through a variant" do
# 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
it "tests bouncing a variant" do
expect(@obj.bounce_variant("cuckoo")[0]).to eq("cuckoo")
expect(@obj.bounce_variant(["coucou", "kuku"])[0]).to eq(["coucou", "kuku"])
expect(@obj.bounce_variant([])[0]).to eq([])
empty_hash = {}
expect(@obj.bounce_variant(empty_hash)[0]).to eq(empty_hash)
end
# these are ambiguous
it "tests pairs with a string" do
# deprecated
expect(@obj.bounce_variant(["s", "foo"])[0]).to eq("foo")
expect(@obj.bounce_variant(DBus.variant("s", "foo"))[0]).to eq("foo")
expect(@obj.bounce_variant([DBus.type("s"), "foo"])[0]).to eq("foo")
# 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
expect(@obj.variant_size(["s", "four"])[0]).to eq(4)
# "av" is the simplest thing that will work,
# shifting the heuristic from a pair to the individual items
expect(@obj.variant_size(["av", ["s", "four"]])[0]).to eq(2)
end
it "tests marshalling an array of variants" do
# 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
expect(@obj.Sybilla(choices)[0]).to eq("Do Plan A")
# automatic typing
expect(@obj.Sybilla(["Plan A", "Plan B"])[0]).to eq("Do Plan A")
end
it "tests service returning nonarray" do
# "warning: default `to_a' will be obsolete"
@obj.the_answer
end
it "tests multibyte string" do
str = @obj.multibyte_string[0]
expect(str).to eq("ã‚ã„ã†ãˆãŠ")
end
end
ruby-dbus-0.11.0/test/server_spec.rb 0000755 0000041 0000041 00000002462 12301727223 017367 0 ustar www-data www-data #!/usr/bin/env rspec
# Test that a server survives various error cases
require_relative "spec_helper"
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
# 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
describe "ServerTest" do
before(:each) do
@bus = DBus::ASessionBus.new
@svc = @bus.request_service "org.ruby.server-test"
end
after(:each) do
@bus.proxy.ReleaseName "org.ruby.server-test"
end
it "tests unexporting an object" do
obj = Foo.new "/org/ruby/Foo"
@svc.export obj
expect(@svc.unexport(obj)).to be_true
end
it "tests unexporting an object not exported" do
obj = Foo.new "/org/ruby/Foo"
expect(@svc.unexport(obj)).to be_false
end
it "tests emiting signals" do
obj = Foo.new "/org/ruby/Foo"
@svc.export obj
obj.signal_without_arguments
obj.signal_with_argument(-0.1)
end
end
ruby-dbus-0.11.0/test/byte_array_spec.rb 0000755 0000041 0000041 00000001773 12301727223 020226 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe "ByteArrayTest" do
before(:each) do
@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
it "tests passing byte array" do
data = [0, 77, 255]
result = @obj.mirror_byte_array(data).first
expect(result).to eq(data)
end
it "tests passing byte array from string" do
data = "AAA"
result = @obj.mirror_byte_array(data).first
expect(result).to eq([65, 65, 65])
end
it "tests passing byte array from hash" do
# Hash is an Enumerable, but is caught earlier
data = { "this will" => "fail" }
expect { @obj.mirror_byte_array(data).first }.to raise_error(DBus::TypeException)
end
it "tests passing byte array from nonenumerable" do
data = Time.now
expect { @obj.mirror_byte_array(data).first }.to raise_error(DBus::TypeException)
end
end
ruby-dbus-0.11.0/test/service_newapi.rb 0000755 0000041 0000041 00000015312 12301727223 020050 0 ustar www-data www-data #!/usr/bin/env ruby
# -*- coding: utf-8 -*-
require_relative "spec_helper"
SimpleCov.command_name "Service Tests" if Object.const_defined? "SimpleCov"
# find the library without external help
$:.unshift File.expand_path("../../lib", __FILE__)
require 'dbus'
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|
DBus.logger.debug variant.inspect
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
dbus_method :mirror_byte_array, "in bytes:ay, out mirrored:ay" do |bytes|
[bytes]
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.Duplicates" do
dbus_method :the_answer, "out answer:i" do
[0]
end
dbus_method :interfaces, "out answer:i" do
raise "This DBus method is currently shadowed by ProxyObject#interfaces"
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
self.LongTaskStart
DBus.logger.debug "Long task began"
task = Thread.new do
DBus.logger.debug "Long task thread started (#{delay}s)"
sleep delay
DBus.logger.debug "Long task will signal end"
self.LongTaskEnd
end
task.abort_on_exception = true # protect from test case bugs
end
dbus_signal :LongTaskStart
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?
DBus.logger.debug "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.11.0/test/introspection_spec.rb 0000755 0000041 0000041 00000001763 12301727223 020764 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe "IntrospectionTest" do
before(:each) do
session_bus = DBus::ASessionBus.new
svc = session_bus.service("org.ruby.service")
@obj = svc.object("/org/ruby/MyInstance")
@obj.introspect
@obj.default_iface = "org.ruby.SampleInterface"
end
it "tests wrong number of arguments" do
expect { @obj.test_variant "too","many","args" }.to raise_error(ArgumentError)
# not enough
expect { @obj.test_variant }.to raise_error(ArgumentError)
end
it "tests shortcut methods" do
@obj.default_iface = nil
expect(@obj.bounce_variant("varargs")).to eq(["varargs"])
# test for a duplicated method name
expect { @obj.the_answer }.to raise_error(NoMethodError)
# ensure istance methods of ProxyObject aren't overwritten by remote
# methods
expect { @obj.interfaces }.not_to raise_error
@obj.default_iface = "org.ruby.SampleInterface"
expect(@obj.the_answer).to eq([42])
end
end
ruby-dbus-0.11.0/test/server_robustness_spec.rb 0000755 0000041 0000041 00000004234 12301727223 021655 0 ustar www-data www-data #!/usr/bin/env rspec
# Test that a server survives various error cases
require_relative "spec_helper"
require "dbus"
describe "ServerRobustnessTest" do
before(:each) do
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
end
# https://trac.luon.net/ruby-dbus/ticket/31
# the server should not crash
it "tests no such path with introspection" do
obj = @svc.object "/org/ruby/NotMyInstance"
expect { obj.introspect }.to raise_error(DBus::Error) do |e|
expect(e).to_not match(/timeout/)
end
end
it "tests no such path without introspection" do
obj = @svc.object "/org/ruby/NotMyInstance"
ifc = DBus::ProxyObjectInterface.new(obj, "org.ruby.SampleInterface")
ifc.define_method("the_answer", "out n:i")
expect { ifc.the_answer }.to raise_error(DBus::Error) do |e|
expect(e).to_not match(/timeout/)
end
end
it "tests a method that raises" do
obj = @svc.object "/org/ruby/MyInstance"
obj.introspect
obj.default_iface = "org.ruby.SampleInterface"
expect { obj.will_raise }.to raise_error(DBus::Error) do |e|
expect(e).to_not match(/timeout/)
end
end
it "tests a method that raises name error" do
obj = @svc.object "/org/ruby/MyInstance"
obj.introspect
obj.default_iface = "org.ruby.SampleInterface"
expect { obj.will_raise_name_error }.to raise_error(DBus::Error) do |e|
expect(e).to_not match(/timeout/)
end
end
# https://trac.luon.net/ruby-dbus/ticket/31#comment:3
it "tests no such method without introspection" do
obj = @svc.object "/org/ruby/MyInstance"
ifc = DBus::ProxyObjectInterface.new(obj, "org.ruby.SampleInterface")
ifc.define_method("not_the_answer", "out n:i")
expect { ifc.not_the_answer }.to raise_error(DBus::Error) do |e|
expect(e).to_not match(/timeout/)
end
end
it "tests no such interface without introspection" do
obj = @svc.object "/org/ruby/MyInstance"
ifc = DBus::ProxyObjectInterface.new(obj, "org.ruby.NoSuchInterface")
ifc.define_method("the_answer", "out n:i")
expect { ifc.the_answer }.to raise_error(DBus::Error) do |e|
expect(e).to_not match(/timeout/)
end
end
end
ruby-dbus-0.11.0/test/thread_safety_spec.rb 0000755 0000041 0000041 00000001416 12301727223 020701 0 ustar www-data www-data #!/usr/bin/env rspec
# Test thread safety
require_relative "spec_helper"
require "dbus"
describe "ThreadSafetyTest" do
it "tests thread competition" do
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
expect(obj.the_answer[0]).to eq(42)
sleep 0.1 * rand
end
end
end
jobs.each do |thread| thread.join end
end
end
ruby-dbus-0.11.0/test/type_spec.rb 0000755 0000041 0000041 00000000673 12301727223 017044 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe DBus do
describe ".type" do
%w{i ai a(ii) aai}.each do |s|
it "parses some type #{s}" do
expect(DBus::type(s).to_s).to be_eql s
end
end
%w{aa (ii ii) hrmp}.each do |s|
it "raises exception for invalid type #{s}" do
expect {DBus::type(s).to_s}.to raise_error DBus::Type::SignatureException
end
end
end
end
ruby-dbus-0.11.0/test/variant_spec.rb 0000755 0000041 0000041 00000003521 12301727223 017522 0 ustar www-data www-data #!/usr/bin/env rspec
# Test marshalling variants according to ruby types
require_relative "spec_helper"
require "dbus"
describe "VariantTest" do
before(:each) do
@bus = DBus::ASessionBus.new
@svc = @bus.service("org.ruby.service")
end
def make_variant(a)
DBus::PacketMarshaller.make_variant(a)
end
it "tests make variant scalar" do
# special case: do not fail immediately, marshaller will do that
expect(make_variant(nil)).to eq(["b", nil])
expect(make_variant(true)).to eq(["b", true])
# Integers
# no byte
expect(make_variant(42)).to eq(["i", 42])
# 3_000_000_000 can be u or x.
# less specific test: just run it thru a loopback
expect(make_variant(3_000_000_000)).to eq(["x", 3_000_000_000])
expect(make_variant(5_000_000_000)).to eq(["x", 5_000_000_000])
expect(make_variant(3.14)).to eq(["d", 3.14])
expect(make_variant("foo")).to eq(["s", "foo"])
expect(make_variant(:bar)).to eq(["s", "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
it "tests make variant array" do
ai = [1, 2, 3]
# as = ["one", "two", "three"]
# which?
# expect(make_variant(ai)).to eq(["ai", [1, 2, 3]])
expect(make_variant(ai)).to eq(["av", [["i", 1],
["i", 2],
["i", 3]]])
a0 = []
expect(make_variant(a0)).to eq(["av", []])
end
it "tests make variant hash" do
h = {"k1" => "v1", "k2" => "v2"}
expect(make_variant(h)).to eq(["a{sv}", {
"k1" => ["s", "v1"],
"k2" => ["s", "v2"],
}])
h0 = {}
expect(make_variant(h0)).to eq(["a{sv}", {}])
end
end
ruby-dbus-0.11.0/test/binding_spec.rb 0000755 0000041 0000041 00000004543 12301727223 017475 0 ustar www-data www-data #!/usr/bin/env rspec
# Test the binding of dbus concepts to ruby concepts
require_relative "spec_helper"
require "dbus"
describe "BindingTest" do
before(:each) do
@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
it "tests class inheritance" do
derived = @svc.object "/org/ruby/MyDerivedInstance"
derived.introspect
# it should inherit from the parent
expect(derived["org.ruby.SampleInterface"]).not_to be_nil
end
# https://trac.luon.net/ruby-dbus/ticket/36
# Interfaces and methods/signals appeared on all classes
it "tests separation of classes" do
test2 = @svc.object "/org/ruby/MyInstance2"
test2.introspect
# it should have its own interface
expect(test2["org.ruby.Test2"]).not_to be_nil
# but not an interface of the Test class
expect(test2["org.ruby.SampleInterface"]).to be_nil
# and the parent should not get polluted by the child
expect(@base["org.ruby.Test2"]).to be_nil
end
it "tests translating errors into exceptions" do
# this is a generic call that will reply with the specified error
expect { @base.Error "org.example.Fail", "as you wish" }.to raise_error(DBus::Error) do |e|
expect(e.name).to eq("org.example.Fail")
expect(e.message).to match(/as you wish/)
end
end
it "tests generic dbus error" do
# this is a generic call that will reply with the specified error
expect { @base.will_raise_error_failed }.to raise_error(DBus::Error) do |e|
expect(e.name).to eq("org.freedesktop.DBus.Error.Failed")
expect(e.message).to match(/failed as designed/)
end
end
it "tests dynamic interface definition" do
# interfaces can be defined dynamicaly
derived = DBus::Object.new "/org/ruby/MyDerivedInstance"
#define a new interface
derived.singleton_class.instance_eval do
dbus_interface "org.ruby.DynamicInterface" do
dbus_method :hello2, "in name:s, in name2:s" do |name, name2|
puts "hello(#{name}, #{name2})"
end
end
end
# the object should have the new iface
ifaces = derived.intfs
expect(ifaces && ifaces.include?("org.ruby.DynamicInterface")).to be_true
end
end
ruby-dbus-0.11.0/test/err_msg_spec.rb 0000755 0000041 0000041 00000002655 12301727223 017523 0 ustar www-data www-data #!/usr/bin/env rspec
# should report it missing on org.ruby.SampleInterface
# (on object...) instead of on DBus::Proxy::ObjectInterface
require_relative "spec_helper"
require "dbus"
describe "ErrMsgTest" do
before(:each) do
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
it "tests report dbus interface" do
# a specific exception...
# mentioning DBus and the interface
expect { @obj.NoSuchMethod }.to raise_error(NameError, /DBus interface.*#{@obj.default_iface}/)
end
it "tests report short struct" do
expect { @obj.test_variant ["(ss)", ["too few"] ] }.to raise_error(DBus::TypeException, /1 elements but type info for 2/)
end
it "tests report long struct" do
expect { @obj.test_variant ["(ss)", ["a", "b", "too many"] ] }.to raise_error(DBus::TypeException, /3 elements but type info for 2/)
end
it "tests report nil" do
nils = [
["(s)", [nil] ], # would get disconnected
["i", nil ],
["a{ss}", {"foo" => nil} ],
]
nils.each do |has_nil|
# TODO want backtrace from the perspective of the caller:
# rescue/reraise in send_sync?
expect { @obj.test_variant has_nil }.to raise_error(DBus::TypeException, /Cannot send nil/)
end
end
end
ruby-dbus-0.11.0/test/spec_helper.rb 0000644 0000041 0000041 00000001421 12301727223 017327 0 ustar www-data www-data if ENV["COVERAGE"]
coverage = ENV["COVERAGE"] == "true"
else
# heuristics: enable for interactive builds (but not in OBS) or in Travis
coverage = !!ENV["DISPLAY"] || ENV["TRAVIS"]
end
if coverage
require "simplecov"
SimpleCov.root File.expand_path("../..", __FILE__)
# do not cover specs
SimpleCov.add_filter "_spec.rb"
# do not cover the activesupport helpers
SimpleCov.add_filter "/core_ext/"
# use coveralls for on-line code coverage reporting at Travis CI
if ENV["TRAVIS"]
require "coveralls"
end
SimpleCov.start
end
$:.unshift File.expand_path("../../lib", __FILE__)
if Object.const_defined? "RSpec"
# http://betterspecs.org/#expect
RSpec.configure do |config|
config.expect_with :rspec do |c|
c.syntax = :expect
end
end
end
ruby-dbus-0.11.0/test/tools/ 0000755 0000041 0000041 00000000000 12301727223 015653 5 ustar www-data www-data ruby-dbus-0.11.0/test/tools/dbus-limited-session.conf 0000644 0000041 0000041 00000001626 12301727223 022572 0 ustar www-data www-data
session
unix:tmpdir=/tmp
tcp:host=127.0.0.1
50
ruby-dbus-0.11.0/test/tools/test_server 0000755 0000041 0000041 00000001364 12301727223 020152 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.11.0/test/tools/dbus-launch-simple 0000755 0000041 0000041 00000002064 12301727223 021277 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.11.0/test/tools/test_env 0000755 0000041 0000041 00000000420 12301727223 017424 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.11.0/test/bus_driver_spec.rb 0000755 0000041 0000041 00000001004 12301727223 020214 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe DBus::Service do
let(:bus) { DBus::ASessionBus.new }
describe "#exists?" do
it "is true for an existing service" do
svc = bus.service("org.ruby.service")
svc.object("/").introspect # must activate the service first :-/
expect(svc.exists?).to be_true
end
it "is false for a nonexisting service" do
svc = bus.service("org.ruby.nosuchservice")
expect(svc.exists?).to be_false
end
end
end
ruby-dbus-0.11.0/test/property_spec.rb 0000755 0000041 0000041 00000002776 12301727223 017755 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe "PropertyTest" do
before(:each) do
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
it "tests property reading" do
expect(@iface["ReadMe"]).to eq("READ ME")
end
it "tests property nonreading" do
expect { @iface["WriteMe"] }.to raise_error(DBus::Error, /not readable/)
end
it "tests property writing" do
@iface["ReadOrWriteMe"] = "VALUE"
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
end
# https://github.com/mvidner/ruby-dbus/pull/19
it "tests service select timeout" do
@iface["ReadOrWriteMe"] = "VALUE"
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
# wait for the service to become idle
sleep 6
# fail: "Property value changed; perhaps the service died and got restarted"
expect(@iface["ReadOrWriteMe"]).to eq("VALUE")
end
it "tests property nonwriting" do
expect { @iface["ReadMe"] = "WROTE" }.to raise_error(DBus::Error, /not writable/)
end
it "tests get all" do
all = @iface.all_properties
expect(all.keys.sort).to eq(["ReadMe", "ReadOrWriteMe"])
end
it "tests unknown property reading" do
expect { @iface["Spoon"] }.to raise_error(DBus::Error, /not found/)
end
it "tests unknown property writing" do
expect { @iface["Spoon"] = "FPRK" }.to raise_error(DBus::Error, /not found/)
end
end
ruby-dbus-0.11.0/test/session_bus_spec_manual.rb 0000755 0000041 0000041 00000000565 12301727223 021754 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe DBus::ASessionBus do
context "when DBUS_SESSION_BUS_ADDRESS is unset in ENV (Issue#4)" do
ENV.delete "DBUS_SESSION_BUS_ADDRESS"
it "can connect" do
bus = DBus::ASessionBus.new
svc = bus.service("org.freedesktop.DBus")
expect(svc.exists?).to be_true
end
end
end
ruby-dbus-0.11.0/test/async_spec.rb 0000755 0000041 0000041 00000002050 12301727223 017167 0 ustar www-data www-data #!/usr/bin/env rspec
# Test the binding of dbus concepts to ruby concepts
require_relative "spec_helper"
require "dbus"
describe "AsyncTest" do
before(:each) do
@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
it "tests async_call_to_default_interface" do
loop = DBus::Main.new
loop << @bus
immediate_answer = @obj.the_answer do |msg, retval|
expect(retval).to eq(42)
loop.quit
end
expect(immediate_answer).to be_nil
# wait for the async reply
loop.run
end
it "tests async_call_to_explicit_interface" do
loop = DBus::Main.new
loop << @bus
ifc = @obj["org.ruby.AnotherInterface"]
immediate_answer = ifc.Reverse("abcd") do |msg, retval|
expect(retval).to eq("dcba")
loop.quit
end
expect(immediate_answer).to be_nil
# wait for the async reply
loop.run
end
end
ruby-dbus-0.11.0/test/introspect_xml_parser_spec.rb 0000755 0000041 0000041 00000001124 12301727223 022501 0 ustar www-data www-data #!/usr/bin/env rspec
require_relative "spec_helper"
require "dbus"
describe "IntrospectXMLParserTest" do
it "tests split interfaces" do
xml = <
EOS
interfaces, _ = DBus::IntrospectXMLParser.new(xml).parse
foo = interfaces.find {|i| i.name == "org.example.Foo" }
expect(foo.methods.keys.size).to eq(2)
end
end
ruby-dbus-0.11.0/test/bus_and_xml_backend_spec.rb 0000755 0000041 0000041 00000002376 12301727223 022027 0 ustar www-data www-data #!/usr/bin/env rspec
# Test the bus class
require_relative "spec_helper"
require 'rubygems'
require 'nokogiri'
require "dbus"
describe "BusAndXmlBackendTest" do
before(:each) do
@bus = DBus::ASessionBus.new
end
it "tests introspection reading rexml" do
DBus::IntrospectXMLParser.backend = DBus::IntrospectXMLParser::REXMLParser
@svc = @bus.service("org.ruby.service")
obj = @svc.object("/org/ruby/MyInstance")
obj.default_iface = 'org.ruby.SampleInterface'
obj.introspect
# "should respond to :the_answer"
expect(obj.the_answer[0]).to eq(42)
# "should work with multiple interfaces"
expect(obj["org.ruby.AnotherInterface"].Reverse('foo')[0]).to eq("oof")
end
it "tests introspection reading nokogiri" do
# peek inside the object to see if a cleanup step worked or not
DBus::IntrospectXMLParser.backend = DBus::IntrospectXMLParser::NokogiriParser
@svc = @bus.service("org.ruby.service")
obj = @svc.object("/org/ruby/MyInstance")
obj.default_iface = 'org.ruby.SampleInterface'
obj.introspect
# "should respond to :the_answer"
expect(obj.the_answer[0]).to eq(42)
# "should work with multiple interfaces"
expect(obj["org.ruby.AnotherInterface"].Reverse('foo')[0]).to eq("oof")
end
end
ruby-dbus-0.11.0/test/main_loop_spec.rb 0000755 0000041 0000041 00000004463 12301727223 020041 0 ustar www-data www-data #!/usr/bin/env rspec
# Test the main loop
require_relative "spec_helper"
require "dbus"
describe "MainLoopTest" do
before(:each) do
@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
DBus.logger.debug "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)
@obj.on_signal "LongTaskEnd" do
DBus.logger.debug "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
DBus.logger.debug "Dynamite burning"
sleep 2
DBus.logger.debug "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
DBus.logger.debug "Defusing dynamite"
# if we get here, defuse the bomb
dynamite.exit
# remove signal handler
@obj.on_signal "LongTaskEnd"
end
it "tests loop quit" do
test_loop_quit 1
end
# https://bugzilla.novell.com/show_bug.cgi?id=537401
it "tests loop drained socket" do
test_loop_quit 0
end
end
ruby-dbus-0.11.0/test/signal_spec.rb 0000755 0000041 0000041 00000004367 12301727223 017344 0 ustar www-data www-data #!/usr/bin/env rspec
# Test the signal handlers
require_relative "spec_helper"
require "dbus"
describe "SignalHandlerTest" do
before(:each) do
@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"
@intf = @obj["org.ruby.Loop"]
@loop = DBus::Main.new
@loop << @session_bus
end
# testing for commit 017c83 (kkaempf)
it "tests overriding a handler" do
DBus.logger.debug "Inside test_overriding_a_handler"
counter = 0
@obj.on_signal "LongTaskEnd" do
DBus.logger.debug "+10"
counter += 10
end
@obj.on_signal "LongTaskEnd" do
DBus.logger.debug "+1"
counter += 1
end
DBus.logger.debug "will begin"
@obj.LongTaskBegin 3
quitter = Thread.new do
DBus.logger.debug "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
DBus.logger.debug "will quit"
@loop.quit
end
@loop.run
quitter.join
expect(counter).to eq(1)
end
it "tests on signal overload" do
DBus.logger.debug "Inside test_on_signal_overload"
counter = 0
started = false
@intf.on_signal "LongTaskStart" do
started = true
end
# Same as intf.on_signal("LongTaskEnd"), just the old way
@intf.on_signal @obj.bus, "LongTaskEnd" do
counter += 1
end
@obj.LongTaskBegin 3
quitter = Thread.new do
DBus.logger.debug "sleep before quit"
sleep 1
DBus.logger.debug "will quit"
@loop.quit
end
@loop.run
quitter.join
expect(started).to eq(true)
expect(counter).to eq(1)
expect { @intf.on_signal }.to raise_error(ArgumentError) # not enough
expect { @intf.on_signal 'to', 'many', 'yarrrrr!' }.to raise_error(ArgumentError)
end
it "tests too many rules" do
100.times do
@obj.on_signal "Whichever" do
puts "not called"
end
end
end
it "tests removing a nonexistent rule" do
@obj.on_signal "DoesNotExist"
end
end
ruby-dbus-0.11.0/VERSION 0000644 0000041 0000041 00000000007 12301727223 014601 0 ustar www-data www-data 0.11.0
ruby-dbus-0.11.0/checksums.yaml.gz 0000444 0000041 0000041 00000000413 12301727223 017020 0 ustar www-data www-data ‹ éˆSe;R0Dûœ"cým::zN Y
“ŠÓã@Foßîív»¼½¾Àóåzý¨»§ßýéýëùšm[Í–Óv¬‘bÆI"g„M‰}¸æîŸ¿ÜÁÀ~dÊX«u M9‡DË\¾ùaÀ^p'54ìµÝRuTã
CXí4ɪ²4EAzÎæR¦Áf¾i•°ŒðPÇP¥°R¥Õ9xÆa7€©ù÷°îÀ
Ì´Ð{Õé³M
£¢2!vÕ85éÄâÌ=‰Xl’MY‘ˆN_»Ð€ÝzX:t™Ëåhæ8s6gbðË7¹Å“ü¢ ruby-dbus-0.11.0/README.md 0000644 0000041 0000041 00000005463 12301727223 015023 0 ustar www-data www-data # Ruby D-Bus
[D-Bus](http://dbus.freedesktop.org) is an interprocess communication
mechanism for Linux.
Ruby D-Bus is a pure Ruby library for writing clients and services for D-Bus.
[![Gem Version][GV img]][Gem Version]
[![Build Status][BS img]][Build Status]
[![Dependency Status][DS img]][Dependency Status]
[![Code Climate][CC img]][Code Climate]
[![Coverage Status][CS img]][Coverage Status]
[Gem Version]: https://rubygems.org/gems/ruby-dbus
[Build Status]: https://travis-ci.org/mvidner/ruby-dbus
[travis pull requests]: https://travis-ci.org/mvidner/ruby-dbus/pull_requests
[Dependency Status]: https://gemnasium.com/mvidner/ruby-dbus
[Code Climate]: https://codeclimate.com/github/mvidner/ruby-dbus
[Coverage Status]: https://coveralls.io/r/mvidner/ruby-dbus
[GV img]: https://badge.fury.io/rb/ruby-dbus.png
[BS img]: https://travis-ci.org/mvidner/ruby-dbus.png
[DS img]: https://gemnasium.com/mvidner/ruby-dbus.png
[CC img]: https://codeclimate.com/github/mvidner/ruby-dbus.png
[CS img]: https://coveralls.io/repos/mvidner/ruby-dbus/badge.png?branch=master
## Example
Check whether the system is on battery power
via [UPower](http://upower.freedesktop.org/docs/UPower.html#UPower:OnBattery)
require "dbus"
sysbus = DBus.system_bus
upower_service = sysbus["org.freedesktop.UPower"]
upower_object = upower_service.object "/org/freedesktop/UPower"
upower_object.introspect
upower_interface = upower_object["org.freedesktop.UPower"]
on_battery = upower_interface["OnBattery"]
if on_battery
puts "The computer IS on battery power."
else
puts "The computer IS NOT on battery power."
end
## Requirements
- Ruby 1.9.3 or 2.0
## Installation
- `gem install ruby-dbus`
## Features
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.md
or view it online on
.
## 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.11.0/COPYING 0000644 0000041 0000041 00000063504 12301727223 014577 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!