ruby-dbus 0.17.0 → 0.18.0.beta3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/NEWS.md +38 -0
- data/README.md +1 -1
- data/Rakefile +3 -11
- data/VERSION +1 -1
- data/doc/Reference.md +10 -3
- data/examples/doc/_extract_examples +2 -0
- data/examples/gdbus/gdbus +11 -5
- data/examples/no-introspect/nm-test.rb +2 -0
- data/examples/no-introspect/tracker-test.rb +3 -1
- data/examples/rhythmbox/playpause.rb +2 -1
- data/examples/service/call_service.rb +1 -0
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +1 -1
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +2 -1
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +1 -0
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +12 -7
- data/lib/dbus/bus.rb +114 -70
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/data.rb +744 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +30 -18
- data/lib/dbus/logger.rb +3 -1
- data/lib/dbus/marshall.rb +229 -293
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +44 -37
- data/lib/dbus/message_queue.rb +10 -5
- data/lib/dbus/object.rb +36 -15
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/proxy_object.rb +18 -4
- data/lib/dbus/proxy_object_factory.rb +11 -7
- data/lib/dbus/proxy_object_interface.rb +26 -22
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +164 -80
- data/lib/dbus/xml.rb +28 -17
- data/lib/dbus.rb +13 -7
- data/ruby-dbus.gemspec +4 -2
- data/spec/async_spec.rb +2 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +2 -0
- data/spec/bus_driver_spec.rb +2 -0
- data/spec/bus_name_spec.rb +3 -1
- data/spec/bus_spec.rb +2 -0
- data/spec/byte_array_spec.rb +2 -0
- data/spec/client_robustness_spec.rb +4 -2
- data/spec/data/marshall.yaml +1639 -0
- data/spec/data_spec.rb +353 -0
- data/spec/err_msg_spec.rb +2 -0
- data/spec/introspect_xml_parser_spec.rb +2 -0
- data/spec/introspection_spec.rb +2 -0
- data/spec/main_loop_spec.rb +2 -0
- data/spec/node_spec.rb +23 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/packet_marshaller_spec.rb +34 -0
- data/spec/packet_unmarshaller_spec.rb +262 -0
- data/spec/property_spec.rb +60 -2
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/server_robustness_spec.rb +2 -0
- data/spec/server_spec.rb +2 -0
- data/spec/service_newapi.rb +37 -4
- data/spec/session_bus_spec.rb +3 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +2 -0
- data/spec/spec_helper.rb +19 -3
- data/spec/thread_safety_spec.rb +2 -0
- data/spec/type_spec.rb +69 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +16 -7
data/lib/dbus/bus.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# dbus.rb - Module containing the low-level D-Bus implementation
|
2
4
|
#
|
3
5
|
# This file is part of the ruby-dbus project
|
@@ -9,7 +11,6 @@
|
|
9
11
|
# See the file "COPYING" for the exact licensing terms.
|
10
12
|
|
11
13
|
require "socket"
|
12
|
-
require "thread"
|
13
14
|
require "singleton"
|
14
15
|
|
15
16
|
# = D-Bus main module
|
@@ -48,6 +49,7 @@ module DBus
|
|
48
49
|
end
|
49
50
|
|
50
51
|
# Retrieves an object at the given _path_.
|
52
|
+
# @param path [ObjectPath]
|
51
53
|
# @return [ProxyObject]
|
52
54
|
def [](path)
|
53
55
|
object(path, api: ApiOptions::A1)
|
@@ -55,9 +57,11 @@ module DBus
|
|
55
57
|
|
56
58
|
# Retrieves an object at the given _path_
|
57
59
|
# whose methods always return an array.
|
60
|
+
# @param path [ObjectPath]
|
61
|
+
# @param api [ApiOptions]
|
58
62
|
# @return [ProxyObject]
|
59
63
|
def object(path, api: ApiOptions::A0)
|
60
|
-
node = get_node(path,
|
64
|
+
node = get_node(path, create: true)
|
61
65
|
if node.object.nil? || node.object.api != api
|
62
66
|
node.object = ProxyObject.new(
|
63
67
|
@bus, @name, path,
|
@@ -67,35 +71,43 @@ module DBus
|
|
67
71
|
node.object
|
68
72
|
end
|
69
73
|
|
70
|
-
# Export an object
|
74
|
+
# Export an object
|
75
|
+
# @param obj [DBus::Object]
|
71
76
|
def export(obj)
|
72
77
|
obj.service = self
|
73
|
-
get_node(obj.path, true).object = obj
|
78
|
+
get_node(obj.path, create: true).object = obj
|
74
79
|
end
|
75
80
|
|
76
81
|
# Undo exporting an object _obj_.
|
77
82
|
# Raises ArgumentError if it is not a DBus::Object.
|
78
83
|
# Returns the object, or false if _obj_ was not exported.
|
84
|
+
# @param obj [DBus::Object]
|
79
85
|
def unexport(obj)
|
80
86
|
raise ArgumentError, "DBus::Service#unexport() expects a DBus::Object argument" unless obj.is_a?(DBus::Object)
|
81
87
|
return false unless obj.path
|
88
|
+
|
82
89
|
last_path_separator_idx = obj.path.rindex("/")
|
83
90
|
parent_path = obj.path[1..last_path_separator_idx - 1]
|
84
91
|
node_name = obj.path[last_path_separator_idx + 1..-1]
|
85
92
|
|
86
|
-
parent_node = get_node(parent_path, false)
|
93
|
+
parent_node = get_node(parent_path, create: false)
|
87
94
|
return false unless parent_node
|
95
|
+
|
88
96
|
obj.service = nil
|
89
97
|
parent_node.delete(node_name).object
|
90
98
|
end
|
91
99
|
|
92
|
-
# Get the object node corresponding to the given
|
93
|
-
#
|
94
|
-
|
100
|
+
# Get the object node corresponding to the given *path*.
|
101
|
+
# @param path [ObjectPath]
|
102
|
+
# @param create [Boolean] if true, the the {Node}s in the path are created
|
103
|
+
# if they do not already exist.
|
104
|
+
# @return [Node,nil]
|
105
|
+
def get_node(path, create: false)
|
95
106
|
n = @root
|
96
107
|
path.sub(%r{^/}, "").split("/").each do |elem|
|
97
108
|
if !(n[elem])
|
98
109
|
return nil if !create
|
110
|
+
|
99
111
|
n[elem] = Node.new(elem)
|
100
112
|
end
|
101
113
|
n = n[elem]
|
@@ -120,13 +132,14 @@ module DBus
|
|
120
132
|
subnodes.each do |nodename|
|
121
133
|
subnode = node[nodename] = Node.new(nodename)
|
122
134
|
subpath = if path == "/"
|
123
|
-
"
|
135
|
+
"/#{nodename}"
|
124
136
|
else
|
125
|
-
path
|
137
|
+
"#{path}/#{nodename}"
|
126
138
|
end
|
127
139
|
rec_introspect(subnode, subpath)
|
128
140
|
end
|
129
141
|
return if intfs.empty?
|
142
|
+
|
130
143
|
node.object = ProxyObjectFactory.new(xml, @bus, @name, path).build
|
131
144
|
end
|
132
145
|
end
|
@@ -135,13 +148,17 @@ module DBus
|
|
135
148
|
#
|
136
149
|
# Class representing a node on an object path.
|
137
150
|
class Node < Hash
|
138
|
-
#
|
151
|
+
# @return [DBus::Object,DBus::ProxyObject,nil]
|
152
|
+
# The D-Bus object contained by the node.
|
139
153
|
attr_accessor :object
|
154
|
+
|
140
155
|
# The name of the node.
|
156
|
+
# @return [String] the last component of its object path, or "/"
|
141
157
|
attr_reader :name
|
142
158
|
|
143
159
|
# Create a new node with a given _name_.
|
144
160
|
def initialize(name)
|
161
|
+
super()
|
145
162
|
@name = name
|
146
163
|
@object = nil
|
147
164
|
end
|
@@ -157,14 +174,12 @@ module DBus
|
|
157
174
|
each_pair do |k, _v|
|
158
175
|
xml += " <node name=\"#{k}\" />\n"
|
159
176
|
end
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
xml += " </interface>\n"
|
167
|
-
end
|
177
|
+
@object&.intfs&.each_pair do |_k, v|
|
178
|
+
xml += " <interface name=\"#{v.name}\">\n"
|
179
|
+
v.methods.each_value { |m| xml += m.to_xml }
|
180
|
+
v.signals.each_value { |m| xml += m.to_xml }
|
181
|
+
v.properties.each_value { |m| xml += m.to_xml }
|
182
|
+
xml += " </interface>\n"
|
168
183
|
end
|
169
184
|
xml += "</node>"
|
170
185
|
xml
|
@@ -182,9 +197,12 @@ module DBus
|
|
182
197
|
if !@object.nil?
|
183
198
|
s += format("%x ", @object.object_id)
|
184
199
|
end
|
185
|
-
|
200
|
+
contents_sub_inspect = keys
|
201
|
+
.map { |k| "#{k} => #{self[k].sub_inspect}" }
|
202
|
+
.join(",")
|
203
|
+
"#{s}{#{contents_sub_inspect}}"
|
186
204
|
end
|
187
|
-
end
|
205
|
+
end
|
188
206
|
|
189
207
|
# FIXME: rename Connection to Bus?
|
190
208
|
|
@@ -207,7 +225,15 @@ module DBus
|
|
207
225
|
def initialize(path)
|
208
226
|
@message_queue = MessageQueue.new(path)
|
209
227
|
@unique_name = nil
|
228
|
+
|
229
|
+
# @return [Hash{Integer => Proc}]
|
230
|
+
# key: message serial
|
231
|
+
# value: block to be run when the reply to that message is received
|
210
232
|
@method_call_replies = {}
|
233
|
+
|
234
|
+
# @return [Hash{Integer => Message}]
|
235
|
+
# for debugging only: messages for which a reply was not received yet;
|
236
|
+
# key == value.serial
|
211
237
|
@method_call_msgs = {}
|
212
238
|
@signal_matchrules = {}
|
213
239
|
@proxy = nil
|
@@ -218,7 +244,7 @@ module DBus
|
|
218
244
|
# but do not block on the queue.
|
219
245
|
# Called by a main loop when something is available in the queue
|
220
246
|
def dispatch_message_queue
|
221
|
-
while (msg = @message_queue.pop(:
|
247
|
+
while (msg = @message_queue.pop(blocking: false)) # FIXME: EOFError
|
222
248
|
process(msg)
|
223
249
|
end
|
224
250
|
end
|
@@ -325,7 +351,7 @@ module DBus
|
|
325
351
|
</signal>
|
326
352
|
</interface>
|
327
353
|
</node>
|
328
|
-
'
|
354
|
+
'
|
329
355
|
# This apostroph is for syntax highlighting editors confused by above xml: "
|
330
356
|
|
331
357
|
# @api private
|
@@ -340,6 +366,7 @@ module DBus
|
|
340
366
|
if reply_handler.nil?
|
341
367
|
send_sync(message) do |rmsg|
|
342
368
|
raise rmsg if rmsg.is_a?(Error)
|
369
|
+
|
343
370
|
ret = rmsg.params
|
344
371
|
end
|
345
372
|
else
|
@@ -444,44 +471,52 @@ module DBus
|
|
444
471
|
end
|
445
472
|
|
446
473
|
# @api private
|
447
|
-
# Send a message
|
474
|
+
# Send a message _msg_ on to the bus. This is done synchronously, thus
|
448
475
|
# the call will block until a reply message arrives.
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
|
453
|
-
|
476
|
+
# @param msg [Message]
|
477
|
+
# @param retc [Proc] the reply handler
|
478
|
+
# @yieldparam rmsg [MethodReturnMessage] the reply
|
479
|
+
# @yieldreturn [Array<Object>] the reply (out) parameters
|
480
|
+
def send_sync(msg, &retc) # :yields: reply/return message
|
481
|
+
return if msg.nil? # check if somethings wrong
|
482
|
+
|
483
|
+
@message_queue.push(msg)
|
484
|
+
@method_call_msgs[msg.serial] = msg
|
485
|
+
@method_call_replies[msg.serial] = retc
|
454
486
|
|
455
487
|
retm = wait_for_message
|
456
488
|
return if retm.nil? # check if somethings wrong
|
457
489
|
|
458
490
|
process(retm)
|
459
|
-
while @method_call_replies.key?
|
491
|
+
while @method_call_replies.key? msg.serial
|
460
492
|
retm = wait_for_message
|
461
493
|
process(retm)
|
462
494
|
end
|
463
495
|
rescue EOFError
|
464
|
-
new_err = DBus::Error.new("Connection dropped after we sent #{
|
496
|
+
new_err = DBus::Error.new("Connection dropped after we sent #{msg.inspect}")
|
465
497
|
raise new_err
|
466
498
|
end
|
467
499
|
|
468
500
|
# @api private
|
469
501
|
# Specify a code block that has to be executed when a reply for
|
470
|
-
# message
|
471
|
-
|
502
|
+
# message _msg_ is received.
|
503
|
+
# @param msg [Message]
|
504
|
+
def on_return(msg, &retc)
|
472
505
|
# Have a better exception here
|
473
|
-
if
|
506
|
+
if msg.message_type != Message::METHOD_CALL
|
474
507
|
raise "on_return should only get method_calls"
|
475
508
|
end
|
476
|
-
|
477
|
-
@
|
509
|
+
|
510
|
+
@method_call_msgs[msg.serial] = msg
|
511
|
+
@method_call_replies[msg.serial] = retc
|
478
512
|
end
|
479
513
|
|
480
514
|
# Asks bus to send us messages matching mr, and execute slot when
|
481
515
|
# received
|
482
|
-
|
516
|
+
# @param match_rule [MatchRule,#to_s]
|
517
|
+
def add_match(match_rule, &slot)
|
483
518
|
# check this is a signal.
|
484
|
-
mrs =
|
519
|
+
mrs = match_rule.to_s
|
485
520
|
DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}"
|
486
521
|
# don't ask for the same match if we override it
|
487
522
|
unless @signal_matchrules.key?(mrs)
|
@@ -491,70 +526,77 @@ module DBus
|
|
491
526
|
@signal_matchrules[mrs] = slot
|
492
527
|
end
|
493
528
|
|
494
|
-
|
495
|
-
|
529
|
+
# @param match_rule [MatchRule,#to_s]
|
530
|
+
def remove_match(match_rule)
|
531
|
+
mrs = match_rule.to_s
|
496
532
|
rule_existed = @signal_matchrules.delete(mrs).nil?
|
497
533
|
# don't remove nonexisting matches.
|
498
534
|
return if rule_existed
|
535
|
+
|
499
536
|
# FIXME: if we do try, the Error.MatchRuleNotFound is *not* raised
|
500
537
|
# and instead is reported as "no return code for nil"
|
501
538
|
proxy.RemoveMatch(mrs)
|
502
539
|
end
|
503
540
|
|
504
541
|
# @api private
|
505
|
-
# Process a message
|
506
|
-
|
507
|
-
|
508
|
-
|
542
|
+
# Process a message _msg_ based on its type.
|
543
|
+
# @param msg [Message]
|
544
|
+
def process(msg)
|
545
|
+
return if msg.nil? # check if somethings wrong
|
546
|
+
|
547
|
+
case msg.message_type
|
509
548
|
when Message::ERROR, Message::METHOD_RETURN
|
510
|
-
raise InvalidPacketException if
|
511
|
-
|
549
|
+
raise InvalidPacketException if msg.reply_serial.nil?
|
550
|
+
|
551
|
+
mcs = @method_call_replies[msg.reply_serial]
|
512
552
|
if !mcs
|
513
|
-
DBus.logger.debug "no return code for mcs: #{mcs.inspect}
|
553
|
+
DBus.logger.debug "no return code for mcs: #{mcs.inspect} msg: #{msg.inspect}"
|
514
554
|
else
|
515
|
-
if
|
516
|
-
mcs.call(Error.new(
|
555
|
+
if msg.message_type == Message::ERROR
|
556
|
+
mcs.call(Error.new(msg))
|
517
557
|
else
|
518
|
-
mcs.call(
|
558
|
+
mcs.call(msg)
|
519
559
|
end
|
520
|
-
@method_call_replies.delete(
|
521
|
-
@method_call_msgs.delete(
|
560
|
+
@method_call_replies.delete(msg.reply_serial)
|
561
|
+
@method_call_msgs.delete(msg.reply_serial)
|
522
562
|
end
|
523
563
|
when DBus::Message::METHOD_CALL
|
524
|
-
if
|
564
|
+
if msg.path == "/org/freedesktop/DBus"
|
525
565
|
DBus.logger.debug "Got method call on /org/freedesktop/DBus"
|
526
566
|
end
|
527
|
-
node = @service.get_node(
|
567
|
+
node = @service.get_node(msg.path, create: false)
|
528
568
|
if !node
|
529
|
-
reply = Message.error(
|
530
|
-
"Object #{
|
569
|
+
reply = Message.error(msg, "org.freedesktop.DBus.Error.UnknownObject",
|
570
|
+
"Object #{msg.path} doesn't exist")
|
531
571
|
@message_queue.push(reply)
|
532
572
|
# handle introspectable as an exception:
|
533
|
-
elsif
|
534
|
-
|
535
|
-
reply = Message.new(Message::METHOD_RETURN).reply_to(
|
573
|
+
elsif msg.interface == "org.freedesktop.DBus.Introspectable" &&
|
574
|
+
msg.member == "Introspect"
|
575
|
+
reply = Message.new(Message::METHOD_RETURN).reply_to(msg)
|
536
576
|
reply.sender = @unique_name
|
537
|
-
xml = node.to_xml(
|
577
|
+
xml = node.to_xml(msg.path)
|
538
578
|
reply.add_param(Type::STRING, xml)
|
539
579
|
@message_queue.push(reply)
|
540
580
|
else
|
541
581
|
obj = node.object
|
542
582
|
return if obj.nil? # FIXME, pushes no reply
|
543
|
-
|
583
|
+
|
584
|
+
obj&.dispatch(msg)
|
544
585
|
end
|
545
586
|
when DBus::Message::SIGNAL
|
546
587
|
# the signal can match multiple different rules
|
547
588
|
# clone to allow new signale handlers to be registered
|
548
589
|
@signal_matchrules.dup.each do |mrs, slot|
|
549
|
-
if DBus::MatchRule.new.from_s(mrs).match(
|
550
|
-
slot.call(
|
590
|
+
if DBus::MatchRule.new.from_s(mrs).match(msg)
|
591
|
+
slot.call(msg)
|
551
592
|
end
|
552
593
|
end
|
553
594
|
else
|
554
|
-
|
595
|
+
# spec(Message Format): Unknown types must be ignored.
|
596
|
+
DBus.logger.debug "Unknown message type: #{msg.message_type}"
|
555
597
|
end
|
556
|
-
rescue Exception =>
|
557
|
-
raise
|
598
|
+
rescue Exception => e
|
599
|
+
raise msg.annotate_exception(e)
|
558
600
|
end
|
559
601
|
|
560
602
|
# Retrieves the Service with the given _name_.
|
@@ -604,7 +646,7 @@ module DBus
|
|
604
646
|
end
|
605
647
|
@service = Service.new(@unique_name, self)
|
606
648
|
end
|
607
|
-
end
|
649
|
+
end
|
608
650
|
|
609
651
|
# = D-Bus session bus class
|
610
652
|
#
|
@@ -630,6 +672,7 @@ module DBus
|
|
630
672
|
# traditional dbus uses /var/lib/dbus/machine-id
|
631
673
|
machine_id_path = Dir["{/etc,/var/lib/dbus,/var/db/dbus}/machine-id"].first
|
632
674
|
return nil unless machine_id_path
|
675
|
+
|
633
676
|
machine_id = File.read(machine_id_path).chomp
|
634
677
|
|
635
678
|
display = ENV["DISPLAY"][/:(\d+)\.?/, 1]
|
@@ -661,7 +704,7 @@ module DBus
|
|
661
704
|
class ASystemBus < Connection
|
662
705
|
# Get the default system bus.
|
663
706
|
def initialize
|
664
|
-
super(
|
707
|
+
super(SYSTEM_BUS_ADDRESS)
|
665
708
|
send_hello
|
666
709
|
end
|
667
710
|
end
|
@@ -736,6 +779,7 @@ module DBus
|
|
736
779
|
while !@quitting && !@buses.empty?
|
737
780
|
ready = IO.select(@buses.keys, [], [], 5) # timeout 5 seconds
|
738
781
|
next unless ready # timeout exceeds so continue unless quitting
|
782
|
+
|
739
783
|
ready.first.each do |socket|
|
740
784
|
b = @buses[socket]
|
741
785
|
begin
|
@@ -750,5 +794,5 @@ module DBus
|
|
750
794
|
end
|
751
795
|
end
|
752
796
|
end
|
753
|
-
end
|
754
|
-
end
|
797
|
+
end
|
798
|
+
end
|
data/lib/dbus/bus_name.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
# This file is part of the ruby-dbus project
|
2
4
|
# Copyright (C) 2019 Martin Vidner
|
3
5
|
#
|
@@ -7,21 +9,23 @@
|
|
7
9
|
# See the file "COPYING" for the exact licensing terms.
|
8
10
|
|
9
11
|
module DBus
|
10
|
-
#
|
12
|
+
# D-Bus: a name for a connection, like ":1.3" or "org.example.ManagerManager".
|
13
|
+
# Implemented as a {::String} that validates at initialization time.
|
11
14
|
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-names-bus
|
12
15
|
class BusName < String
|
13
16
|
# @raise Error if not a valid bus name
|
14
|
-
def initialize(
|
15
|
-
unless self.class.valid?(
|
16
|
-
raise DBus::Error, "Invalid bus name #{
|
17
|
+
def initialize(name)
|
18
|
+
unless self.class.valid?(name)
|
19
|
+
raise DBus::Error, "Invalid bus name #{name.inspect}"
|
17
20
|
end
|
21
|
+
|
18
22
|
super
|
19
23
|
end
|
20
24
|
|
21
|
-
def self.valid?(
|
22
|
-
|
23
|
-
(
|
24
|
-
|
25
|
+
def self.valid?(name)
|
26
|
+
name.size <= 255 &&
|
27
|
+
(name =~ /\A:[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)+\z/ ||
|
28
|
+
name =~ /\A[A-Za-z_-][A-Za-z0-9_-]*(\.[A-Za-z_-][A-Za-z0-9_-]*)+\z/)
|
25
29
|
end
|
26
30
|
end
|
27
31
|
end
|