ruby-dbus 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/NEWS CHANGED
@@ -5,6 +5,22 @@ Note about bug numbers:
5
5
  Issue#1 - http://github.com/mvidner/ruby-dbus/issues#issue/1
6
6
  bnc#1 - https://bugzilla.novell.com/show_bug.cgi?id=1
7
7
 
8
+ == Ruby D-Bus 0.7.0 - 2011-07-26
9
+
10
+ Features:
11
+ * Added ASystemBus and ASessionBus, non-singletons useful in tests
12
+ and threads.
13
+
14
+ Bug fixes:
15
+ * Fixed handling of multibyte strings (Issue#8, by Takayuki YAMAGUCHI).
16
+ * Allow reopening of a dbus_interface declaration (Issue#9, by T. YAMAGUCHI).
17
+ * Fixed ruby-1.9.2 compatibility again (Issue#12).
18
+ * Fixed authentication on BSD (Issue#11, by Jonathan Walker)
19
+ * Fixed exiting a nested event loop for synchronous calls
20
+ (reported by Timo Warns).
21
+ * Fixed introspection calls leaking reply handlers.
22
+ * "rake test" now works, doing what was called "rake env:test"
23
+
8
24
  == Ruby D-Bus 0.6.0 - 2010-12-11
9
25
 
10
26
  Features:
data/README CHANGED
@@ -5,7 +5,7 @@ D-Bus system can be used in the Ruby programming language.
5
5
 
6
6
  == Requirements
7
7
 
8
- * Ruby 1.8 (>= 1.8.6?) or 1.9
8
+ * Ruby 1.8.7 or 1.9
9
9
 
10
10
  == Installation
11
11
 
data/Rakefile CHANGED
@@ -3,33 +3,32 @@ require 'rake'
3
3
  require 'rake/gempackagetask'
4
4
  require 'fileutils'
5
5
  include FileUtils
6
+ require 'tmpdir'
6
7
  require 'rake/rdoctask'
7
8
  require 'rake/testtask'
8
9
 
9
10
  desc 'Default: run tests in the proper environment'
10
- task :default => "env:test"
11
+ task :default => :test
11
12
 
12
13
  def common_test_task(t)
13
14
  t.libs << "lib"
14
- t.test_files = FileList['test/*_test.rb', 'test/t*.rb']
15
+ t.test_files = FileList['test/*_test.rb', 'test/t[0-9]*.rb']
15
16
  t.verbose = true
16
17
  end
17
- Rake::TestTask.new {|t| common_test_task t }
18
+ Rake::TestTask.new("bare:test") {|t| common_test_task t }
18
19
 
19
20
  begin
20
21
  require 'rcov/rcovtask'
21
- Rcov::RcovTask.new {|t| common_test_task t }
22
+ Rcov::RcovTask.new("bare:rcov") {|t| common_test_task t }
22
23
  rescue LoadError
23
24
  # no rcov, never mind
24
25
  end
25
26
 
26
27
  %w(test rcov).each do |tname|
27
- namespace :env do
28
- desc "Run #{tname} in the proper environment"
29
- task tname do |t|
30
- cd "test" do
31
- system "./test_env rake #{tname}"
32
- end
28
+ desc "Run bare:#{tname} in the proper environment"
29
+ task tname do |t|
30
+ cd "test" do
31
+ system "./test_env rake bare:#{tname}"
33
32
  end
34
33
  end
35
34
  end
@@ -40,6 +39,17 @@ Rake::GemPackageTask.new(GEMSPEC) do |pkg|
40
39
  # no other formats needed
41
40
  end
42
41
 
42
+ desc "Build a package from a clone of the local Git repo"
43
+ task :package_git do |t|
44
+ Dir.mktmpdir do |temp|
45
+ sh "git clone . #{temp}"
46
+ cd temp do
47
+ sh "rake package"
48
+ end
49
+ cp_r "#{temp}/pkg", "."
50
+ end
51
+ end
52
+
43
53
  Rake::RDocTask.new do |rd|
44
54
  rd.rdoc_dir = 'doc/rdoc'
45
55
  rd.rdoc_files.include("README", "lib/**/*.rb")
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.6.0
1
+ 0.7.0
data/lib/dbus/auth.rb CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  $debug = $DEBUG #it's all over the state machine
10
10
 
11
+ require 'rbconfig'
12
+
11
13
  module DBus
12
14
  # Exception raised when authentication fails somehow.
13
15
  class AuthenticationFailed < Exception
@@ -62,7 +64,7 @@ module DBus
62
64
  # name of cookie file, id of cookie in file, servers random challenge
63
65
  context, id, s_challenge = data.split(' ')
64
66
  # Random client challenge
65
- c_challenge = Array.new(s_challenge.length/2).map{|obj|obj=rand(255).to_s}.join
67
+ c_challenge = Array.new(s_challenge.bytesize/2).map{|obj|obj=rand(255).to_s}.join
66
68
  # Search cookie file for id
67
69
  path = File.join(ENV['HOME'], '.dbus-keyrings', context)
68
70
  puts "DEBUG: path: #{path.inspect}" if $debug
@@ -118,7 +120,11 @@ module DBus
118
120
 
119
121
  # Start the authentication process.
120
122
  def authenticate
121
- @socket.write(0.chr)
123
+ if (RbConfig::CONFIG["target_os"] =~ /bsd/)
124
+ @socket.sendmsg(0.chr, 0, nil, [:SOCKET, :SCM_CREDS, ""])
125
+ else
126
+ @socket.write(0.chr)
127
+ end
122
128
  next_authenticator
123
129
  @state = :Starting
124
130
  while @state != :Authenticated
@@ -142,12 +148,12 @@ module DBus
142
148
  # Try authentication using the next authenticator.
143
149
  def next_authenticator
144
150
  begin
145
- raise AuthException if @auth_list.size == 0
151
+ raise AuthenticationFailed if @auth_list.size == 0
146
152
  @authenticator = @auth_list.shift.new
147
153
  auth_msg = ["AUTH", @authenticator.name, @authenticator.authenticate]
148
154
  puts "DEBUG: auth_msg: #{auth_msg.inspect}" if $debug
149
155
  send(auth_msg)
150
- rescue AuthException
156
+ rescue AuthenticationFailed
151
157
  @socket.close
152
158
  raise
153
159
  end
@@ -161,7 +167,7 @@ module DBus
161
167
  while left > 0
162
168
  buf = @socket.read( left > 1 ? 1 : left )
163
169
  break if buf.nil?
164
- left -= buf.size
170
+ left -= buf.bytesize
165
171
  data += buf
166
172
  break if data.include? crlf #crlf means line finished, the TCP socket keeps on listening, so we break
167
173
  end
data/lib/dbus/bus.rb CHANGED
@@ -140,6 +140,7 @@ module DBus
140
140
  end
141
141
 
142
142
  # Return an XML string representation of the node.
143
+ # It is shallow, not recursing into subnodes
143
144
  def to_xml
144
145
  xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
145
146
  "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
@@ -383,33 +384,52 @@ module DBus
383
384
  '
384
385
  # This apostroph is for syntax highlighting editors confused by above xml: "
385
386
 
386
- def introspect_data(dest, path)
387
- m = DBus::Message.new(DBus::Message::METHOD_CALL)
388
- m.path = path
389
- m.interface = "org.freedesktop.DBus.Introspectable"
390
- m.destination = dest
391
- m.member = "Introspect"
392
- m.sender = unique_name
393
- if not block_given?
394
- # introspect in synchronous !
395
- send_sync(m) do |rmsg|
387
+ # Send a _message_.
388
+ # If _reply_handler_ is not given, wait for the reply
389
+ # and return the reply, or raise the error.
390
+ # If _reply_handler_ is given, it will be called when the reply
391
+ # eventually arrives, with the reply message as the 1st param
392
+ # and its params following
393
+ def send_sync_or_async(message, &reply_handler)
394
+ ret = nil
395
+ if reply_handler.nil?
396
+ send_sync(message) do |rmsg|
396
397
  if rmsg.is_a?(Error)
397
398
  raise rmsg
398
399
  else
399
- return rmsg.params[0] # return value of introspect_data
400
+ ret = rmsg.params
400
401
  end
401
402
  end
402
403
  else
403
- send(m.marshall)
404
- on_return(m) do |rmsg|
404
+ on_return(message) do |rmsg|
405
405
  if rmsg.is_a?(Error)
406
- yield rmsg
406
+ reply_handler.call(rmsg)
407
407
  else
408
- yield rmsg.params[0]
408
+ reply_handler.call(rmsg, * rmsg.params)
409
409
  end
410
410
  end
411
+ send(message.marshall)
412
+ end
413
+ ret
414
+ end
415
+
416
+ def introspect_data(dest, path, &reply_handler)
417
+ m = DBus::Message.new(DBus::Message::METHOD_CALL)
418
+ m.path = path
419
+ m.interface = "org.freedesktop.DBus.Introspectable"
420
+ m.destination = dest
421
+ m.member = "Introspect"
422
+ m.sender = unique_name
423
+ if reply_handler.nil?
424
+ send_sync_or_async(m).first
425
+ else
426
+ send_sync_or_async(m) do |*args|
427
+ # TODO test async introspection, is it used at all?
428
+ args.shift # forget the message, pass only the text
429
+ reply_handler.call(*args)
430
+ nil
431
+ end
411
432
  end
412
- nil
413
433
  end
414
434
 
415
435
  # Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
@@ -549,9 +569,7 @@ module DBus
549
569
  return if retm.nil? #check if somethings wrong
550
570
 
551
571
  process(retm)
552
- until [DBus::Message::ERROR,
553
- DBus::Message::METHOD_RETURN].include?(retm.message_type) and
554
- retm.reply_serial == m.serial
572
+ while @method_call_replies.has_key? m.serial
555
573
  retm = wait_for_message
556
574
  process(retm)
557
575
  end
@@ -695,10 +713,10 @@ module DBus
695
713
  # = D-Bus session bus class
696
714
  #
697
715
  # The session bus is a session specific bus (mostly for desktop use).
698
- # This is a singleton class.
699
- class SessionBus < Connection
700
- include Singleton
701
-
716
+ #
717
+ # Use SessionBus, the non-singleton ASessionBus is
718
+ # for the test suite.
719
+ class ASessionBus < Connection
702
720
  # Get the the default session bus.
703
721
  def initialize
704
722
  super(ENV["DBUS_SESSION_BUS_ADDRESS"] || address_from_file)
@@ -719,13 +737,20 @@ module DBus
719
737
  end
720
738
  end
721
739
 
740
+ # See ASessionBus
741
+ class SessionBus < ASessionBus
742
+ include Singleton
743
+ end
744
+
745
+
722
746
  # = D-Bus system bus class
723
747
  #
724
748
  # The system bus is a system-wide bus mostly used for global or
725
- # system usages. This is a singleton class.
726
- class SystemBus < Connection
727
- include Singleton
728
-
749
+ # system usages.
750
+ #
751
+ # Use SystemBus, the non-singleton ASystemBus is
752
+ # for the test suite.
753
+ class ASystemBus < Connection
729
754
  # Get the default system bus.
730
755
  def initialize
731
756
  super(SystemSocketName)
@@ -755,6 +780,11 @@ module DBus
755
780
  end
756
781
  end
757
782
 
783
+ # See ASystemBus
784
+ class SystemBus < ASystemBus
785
+ include Singleton
786
+ end
787
+
758
788
  # Shortcut for the SystemBus instance
759
789
  def DBus.system_bus
760
790
  SystemBus.instance
data/lib/dbus/export.rb CHANGED
@@ -15,19 +15,20 @@ module DBus
15
15
  # = Exportable D-Bus object class
16
16
  #
17
17
  # Objects that are going to be exported by a D-Bus service
18
- # should inherit from this class.
18
+ # should inherit from this class. At the client side, use ProxyObject.
19
19
  class Object
20
20
  # The path of the object.
21
21
  attr_reader :path
22
- # The interfaces that the object supports.
22
+ # The interfaces that the object supports. Hash: String => Interface
23
23
  class_attribute :intfs
24
24
  # The service that the object is exported by.
25
25
  attr_writer :service
26
26
 
27
- @@cur_intf = nil
27
+ @@cur_intf = nil # Interface
28
28
  @@intfs_mutex = Mutex.new
29
29
 
30
30
  # Create a new object with a given _path_.
31
+ # Use Service#export to export it.
31
32
  def initialize(path)
32
33
  @path = path
33
34
  @service = nil
@@ -39,7 +40,7 @@ module DBus
39
40
  self.intfs = (self.intfs || {}).merge({intf.name => intf})
40
41
  end
41
42
 
42
- # Dispatch a message _msg_.
43
+ # Dispatch a message _msg_ to call exported methods
43
44
  def dispatch(msg)
44
45
  case msg.message_type
45
46
  when Message::METHOD_CALL
@@ -73,8 +74,10 @@ module DBus
73
74
  # belong to.
74
75
  def self.dbus_interface(s)
75
76
  @@intfs_mutex.synchronize do
76
- @@cur_intf = Interface.new(s)
77
- self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
77
+ unless @@cur_intf = (self.intfs && self.intfs[s])
78
+ @@cur_intf = Interface.new(s)
79
+ self.intfs = (self.intfs || {}).merge({s => @@cur_intf})
80
+ end
78
81
  yield
79
82
  @@cur_intf = nil
80
83
  end
@@ -8,6 +8,7 @@
8
8
  # License, version 2.1 as published by the Free Software Foundation.
9
9
  # See the file "COPYING" for the exact licensing terms.
10
10
 
11
+ # TODO check if it is slow, make replaceable
11
12
  require 'rexml/document'
12
13
 
13
14
  module DBus
@@ -27,15 +28,16 @@ module DBus
27
28
  # = D-Bus interface class
28
29
  #
29
30
  # This class is the interface descriptor. In most cases, the Introspect()
30
- # method call instanciates and configures this class for us.
31
+ # method call instantiates and configures this class for us.
31
32
  #
32
33
  # It also is the local definition of interface exported by the program.
34
+ # At the client side, see ProxyObjectInterface
33
35
  class Interface
34
- # The name of the interface.
36
+ # The name of the interface. String
35
37
  attr_reader :name
36
- # The methods that are part of the interface.
38
+ # The methods that are part of the interface. Hash: Symbol => DBus::Method
37
39
  attr_reader :methods
38
- # The signals that are part of the interface.
40
+ # The signals that are part of the interface. Hash: Symbol => Signal
39
41
  attr_reader :signals
40
42
 
41
43
  # Creates a new interface with a given _name_.
@@ -47,7 +49,7 @@ module DBus
47
49
 
48
50
  # Validates a service _name_.
49
51
  def validate_name(name)
50
- raise InvalidIntrospectionData if name.size > 255
52
+ raise InvalidIntrospectionData if name.bytesize > 255
51
53
  raise InvalidIntrospectionData if name =~ /^\./ or name =~ /\.$/
52
54
  raise InvalidIntrospectionData if name =~ /\.\./
53
55
  raise InvalidIntrospectionData if not name =~ /\./
@@ -100,14 +102,14 @@ module DBus
100
102
  # This is a generic class for entities that are part of the interface
101
103
  # such as methods and signals.
102
104
  class InterfaceElement
103
- # The name of the interface element.
105
+ # The name of the interface element. Symbol
104
106
  attr_reader :name
105
- # The parameters of the interface element
107
+ # The parameters of the interface element. Array: FormalParameter
106
108
  attr_reader :params
107
109
 
108
110
  # Validates element _name_.
109
111
  def validate_name(name)
110
- if (not name =~ MethodSignalRE) or (name.size > 255)
112
+ if (not name =~ MethodSignalRE) or (name.bytesize > 255)
111
113
  raise InvalidMethodName, name
112
114
  end
113
115
  end
@@ -135,7 +137,7 @@ module DBus
135
137
  #
136
138
  # This is a class representing methods that are part of an interface.
137
139
  class Method < InterfaceElement
138
- # The list of return values for the method.
140
+ # The list of return values for the method. Array: FormalParameter
139
141
  attr_reader :rets
140
142
 
141
143
  # Creates a new method interface element with the given _name_.
@@ -227,10 +229,9 @@ module DBus
227
229
  @xml = xml
228
230
  end
229
231
 
230
- # Recursively parses the subnodes, constructing the tree.
232
+ # return the names of direct subnodes
231
233
  def parse_subnodes
232
234
  subnodes = Array.new
233
- t = Time.now
234
235
  d = REXML::Document.new(@xml)
235
236
  d.elements.each("node/node") do |e|
236
237
  subnodes << e.attributes["name"]
@@ -238,9 +239,9 @@ module DBus
238
239
  subnodes
239
240
  end
240
241
 
241
- # Parses the XML, constructing the tree.
242
+ # return a pair: [list of Interfaces, list of direct subnode names]
242
243
  def parse
243
- ret = Array.new
244
+ interfaces = Array.new
244
245
  subnodes = Array.new
245
246
  t = Time.now
246
247
  d = REXML::Document.new(@xml)
@@ -259,13 +260,13 @@ module DBus
259
260
  parse_methsig(se, s)
260
261
  i << s
261
262
  end
262
- ret << i
263
+ interfaces << i
263
264
  end
264
265
  d = Time.now - t
265
266
  if d > 2
266
267
  puts "Some XML took more that two secs to parse. Optimize me!" if $DEBUG
267
268
  end
268
- [ret, subnodes]
269
+ [interfaces, subnodes]
269
270
  end
270
271
 
271
272
  ######################################################################
@@ -342,7 +343,7 @@ module DBus
342
343
  methdef = "def #{m.name}("
343
344
  methdef += (0..(m.params.size - 1)).to_a.collect { |n|
344
345
  "arg#{n}"
345
- }.join(", ")
346
+ }.push("&reply_handler").join(", ")
346
347
  methdef += %{)
347
348
  msg = Message.new(Message::METHOD_CALL)
348
349
  msg.path = @object.path
@@ -365,26 +366,7 @@ module DBus
365
366
  idx += 1
366
367
  end
367
368
  methdef += "
368
- ret = nil
369
- if block_given?
370
- @object.bus.on_return(msg) do |rmsg|
371
- if rmsg.is_a?(Error)
372
- yield(rmsg)
373
- else
374
- yield(rmsg, *rmsg.params)
375
- end
376
- end
377
- @object.bus.send(msg.marshall)
378
- else
379
- @object.bus.send_sync(msg) do |rmsg|
380
- if rmsg.is_a?(Error)
381
- raise rmsg
382
- else
383
- ret = rmsg.params
384
- end
385
- end
386
- end
387
- ret
369
+ @object.bus.send_sync_or_async(msg, &reply_handler)
388
370
  end
389
371
  "
390
372
  singleton_class.class_eval(methdef)
@@ -448,7 +430,7 @@ module DBus
448
430
  # over the bus so that the method is executed remotely on the correctponding
449
431
  # object.
450
432
  class ProxyObject
451
- # The subnodes of the object in the tree.
433
+ # The names of direct subnodes of the object in the tree.
452
434
  attr_accessor :subnodes
453
435
  # Flag determining whether the object has been introspected.
454
436
  attr_accessor :introspected
@@ -458,7 +440,7 @@ module DBus
458
440
  attr_reader :path
459
441
  # The bus the object is reachable via.
460
442
  attr_reader :bus
461
- # The default interface of the object.
443
+ # The default interface of the object, as String.
462
444
  attr_accessor :default_iface
463
445
 
464
446
  # Creates a new proxy object living on the given _bus_ at destination _dest_
@@ -495,11 +477,12 @@ module DBus
495
477
 
496
478
  # Returns whether the object has an interface with the given _name_.
497
479
  def has_iface?(name)
498
- raise "Cannot call has_iface? is not introspected" if not @introspected
480
+ raise "Cannot call has_iface? if not introspected" if not @introspected
499
481
  @interfaces.member?(name)
500
482
  end
501
483
 
502
484
  # Registers a handler, the code block, for a signal with the given _name_.
485
+ # It uses _default_iface_ which must have been set.
503
486
  def on_signal(name, &block)
504
487
  if @default_iface and has_iface?(@default_iface)
505
488
  @interfaces[@default_iface].on_signal(@bus, name, &block)
@@ -521,10 +504,9 @@ module DBus
521
504
  rescue NameError => e
522
505
  # interesting, foo.method("unknown")
523
506
  # raises NameError, not NoMethodError
524
- match = /undefined method `([^']*)' for class `([^']*)'/.match e.to_s
525
- raise unless match and match[2] == "DBus::ProxyObjectInterface"
507
+ raise unless e.to_s =~ /undefined method `#{name}'/
526
508
  # BTW e.exception("...") would preserve the class.
527
- raise NoMethodError,"undefined method `#{match[1]}' for DBus interface `#{@default_iface}' on object `#{@path}'"
509
+ raise NoMethodError,"undefined method `#{name}' for DBus interface `#{@default_iface}' on object `#{@path}'"
528
510
  end
529
511
  else
530
512
  # TODO distinguish: