ruby-dbus 0.16.0 → 0.25.0
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 +288 -3
- data/README.md +3 -5
- data/Rakefile +27 -12
- data/VERSION +1 -1
- data/doc/Reference.md +109 -10
- data/examples/doc/_extract_examples +7 -0
- data/examples/no-bus/pulseaudio.rb +50 -0
- 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 +2 -1
- data/examples/service/complex-property.rb +21 -0
- data/examples/service/service_newapi.rb +3 -3
- data/examples/simple/call_introspect.rb +1 -0
- data/examples/simple/get_id.rb +6 -3
- data/examples/simple/properties.rb +2 -0
- data/examples/utils/listnames.rb +10 -1
- data/examples/utils/notify.rb +1 -0
- data/lib/dbus/api_options.rb +9 -0
- data/lib/dbus/auth.rb +307 -217
- data/lib/dbus/bus.rb +155 -588
- data/lib/dbus/bus_name.rb +12 -8
- data/lib/dbus/connection.rb +363 -0
- data/lib/dbus/core_ext/class/attribute.rb +1 -1
- data/lib/dbus/data.rb +821 -0
- data/lib/dbus/emits_changed_signal.rb +83 -0
- data/lib/dbus/error.rb +4 -2
- data/lib/dbus/introspect.rb +133 -31
- data/lib/dbus/logger.rb +6 -3
- data/lib/dbus/main.rb +66 -0
- data/lib/dbus/marshall.rb +247 -296
- data/lib/dbus/matchrule.rb +16 -16
- data/lib/dbus/message.rb +66 -57
- data/lib/dbus/message_queue.rb +40 -25
- data/lib/dbus/node_tree.rb +105 -0
- data/lib/dbus/object.rb +442 -33
- data/lib/dbus/object_manager.rb +61 -0
- data/lib/dbus/object_path.rb +11 -6
- data/lib/dbus/object_server.rb +149 -0
- data/lib/dbus/org.freedesktop.DBus.xml +97 -0
- data/lib/dbus/platform.rb +26 -0
- data/lib/dbus/proxy_object.rb +30 -11
- data/lib/dbus/proxy_object_factory.rb +13 -7
- data/lib/dbus/proxy_object_interface.rb +63 -30
- data/lib/dbus/proxy_service.rb +107 -0
- data/lib/dbus/raw_message.rb +91 -0
- data/lib/dbus/type.rb +319 -86
- data/lib/dbus/xml.rb +40 -19
- data/lib/dbus.rb +28 -17
- data/ruby-dbus.gemspec +13 -6
- data/spec/async_spec.rb +2 -0
- data/spec/auth_spec.rb +225 -0
- data/spec/binding_spec.rb +2 -0
- data/spec/bus_and_xml_backend_spec.rb +5 -1
- data/spec/bus_connection_spec.rb +93 -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/connection_spec.rb +37 -0
- data/spec/coverage_helper.rb +39 -0
- data/spec/data/marshall.yaml +1667 -0
- data/spec/data_spec.rb +673 -0
- data/spec/dbus_spec.rb +22 -0
- data/spec/emits_changed_signal_spec.rb +58 -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 +17 -1
- data/spec/message_spec.rb +21 -0
- data/spec/mock-service/cockpit-dbustests.rb +29 -0
- data/spec/mock-service/com.redhat.Cockpit.DBusTests.xml +180 -0
- data/spec/mock-service/org.ruby.service.service +4 -0
- data/spec/mock-service/org.rubygems.ruby_dbus.DBusTests.service +4 -0
- data/spec/{service_newapi.rb → mock-service/spaghetti-monster.rb} +122 -75
- data/spec/node_spec.rb +65 -0
- data/spec/object_manager_spec.rb +33 -0
- data/spec/object_path_spec.rb +3 -0
- data/spec/object_server_spec.rb +138 -0
- data/spec/object_spec.rb +194 -0
- data/spec/packet_marshaller_spec.rb +41 -0
- data/spec/packet_unmarshaller_spec.rb +248 -0
- data/spec/platform_spec.rb +14 -0
- data/spec/property_spec.rb +214 -12
- data/spec/proxy_object_interface_spec.rb +35 -0
- data/spec/proxy_object_spec.rb +2 -0
- data/spec/{bus_driver_spec.rb → proxy_service_spec.rb} +15 -8
- data/spec/raw_message_spec.rb +32 -0
- data/spec/server_robustness_spec.rb +20 -6
- data/spec/session_bus_spec.rb +36 -1
- data/spec/session_bus_spec_manual.rb +2 -0
- data/spec/signal_spec.rb +16 -3
- data/spec/spec_helper.rb +25 -33
- data/spec/thread_safety_spec.rb +55 -12
- data/spec/tools/dbus-launch-simple +9 -6
- data/spec/tools/dbus-limited-session.conf +32 -0
- data/spec/tools/test_env +26 -6
- data/spec/type_spec.rb +214 -6
- data/spec/value_spec.rb +16 -1
- data/spec/variant_spec.rb +4 -2
- data/spec/zzz_quit_spec.rb +16 -0
- metadata +92 -20
- data/examples/gdbus/gdbus +0 -257
- data/examples/gdbus/gdbus.glade +0 -98
- data/examples/gdbus/launch.sh +0 -4
- data/spec/server_spec.rb +0 -53
- data/spec/tools/test_server +0 -39
data/lib/dbus/bus.rb
CHANGED
|
@@ -1,421 +1,38 @@
|
|
|
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
|
|
4
6
|
# Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
|
|
5
7
|
#
|
|
6
|
-
# This library is free software; you
|
|
8
|
+
# This library is free software; you can redistribute it and/or
|
|
7
9
|
# modify it under the terms of the GNU Lesser General Public
|
|
8
10
|
# License, version 2.1 as published by the Free Software Foundation.
|
|
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
|
|
|
16
|
+
require_relative "connection"
|
|
17
|
+
|
|
15
18
|
# = D-Bus main module
|
|
16
19
|
#
|
|
17
20
|
# Module containing all the D-Bus modules and classes.
|
|
18
21
|
module DBus
|
|
19
|
-
#
|
|
20
|
-
#
|
|
21
|
-
class
|
|
22
|
-
# The service name.
|
|
23
|
-
attr_reader :name
|
|
24
|
-
# The bus the service is running on.
|
|
25
|
-
attr_reader :bus
|
|
26
|
-
# The service root (FIXME).
|
|
27
|
-
attr_reader :root
|
|
28
|
-
|
|
29
|
-
# Create a new service with a given _name_ on a given _bus_.
|
|
30
|
-
def initialize(name, bus)
|
|
31
|
-
@name = BusName.new(name)
|
|
32
|
-
@bus = bus
|
|
33
|
-
@root = Node.new("/")
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Determine whether the service name already exists.
|
|
37
|
-
def exists?
|
|
38
|
-
bus.proxy.ListNames[0].member?(@name)
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Perform an introspection on all the objects on the service
|
|
42
|
-
# (starting recursively from the root).
|
|
43
|
-
def introspect
|
|
44
|
-
raise NotImplementedError if block_given?
|
|
45
|
-
|
|
46
|
-
rec_introspect(@root, "/")
|
|
47
|
-
self
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Retrieves an object at the given _path_.
|
|
51
|
-
# @return [ProxyObject]
|
|
52
|
-
def [](path)
|
|
53
|
-
object(path, api: ApiOptions::A1)
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Retrieves an object at the given _path_
|
|
57
|
-
# whose methods always return an array.
|
|
58
|
-
# @return [ProxyObject]
|
|
59
|
-
def object(path, api: ApiOptions::A0)
|
|
60
|
-
node = get_node(path, _create = true)
|
|
61
|
-
if node.object.nil? || node.object.api != api
|
|
62
|
-
node.object = ProxyObject.new(
|
|
63
|
-
@bus, @name, path,
|
|
64
|
-
api: api
|
|
65
|
-
)
|
|
66
|
-
end
|
|
67
|
-
node.object
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Export an object _obj_ (an DBus::Object subclass instance).
|
|
71
|
-
def export(obj)
|
|
72
|
-
obj.service = self
|
|
73
|
-
get_node(obj.path, true).object = obj
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
# Undo exporting an object _obj_.
|
|
77
|
-
# Raises ArgumentError if it is not a DBus::Object.
|
|
78
|
-
# Returns the object, or false if _obj_ was not exported.
|
|
79
|
-
def unexport(obj)
|
|
80
|
-
raise ArgumentError, "DBus::Service#unexport() expects a DBus::Object argument" unless obj.is_a?(DBus::Object)
|
|
81
|
-
return false unless obj.path
|
|
82
|
-
last_path_separator_idx = obj.path.rindex("/")
|
|
83
|
-
parent_path = obj.path[1..last_path_separator_idx - 1]
|
|
84
|
-
node_name = obj.path[last_path_separator_idx + 1..-1]
|
|
85
|
-
|
|
86
|
-
parent_node = get_node(parent_path, false)
|
|
87
|
-
return false unless parent_node
|
|
88
|
-
obj.service = nil
|
|
89
|
-
parent_node.delete(node_name).object
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
# Get the object node corresponding to the given _path_. if _create_ is
|
|
93
|
-
# true, the the nodes in the path are created if they do not already exist.
|
|
94
|
-
def get_node(path, create = false)
|
|
95
|
-
n = @root
|
|
96
|
-
path.sub(%r{^/}, "").split("/").each do |elem|
|
|
97
|
-
if !(n[elem])
|
|
98
|
-
return nil if !create
|
|
99
|
-
n[elem] = Node.new(elem)
|
|
100
|
-
end
|
|
101
|
-
n = n[elem]
|
|
102
|
-
end
|
|
103
|
-
if n.nil?
|
|
104
|
-
DBus.logger.debug "Warning, unknown object #{path}"
|
|
105
|
-
end
|
|
106
|
-
n
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
#########
|
|
110
|
-
|
|
111
|
-
private
|
|
112
|
-
|
|
113
|
-
#########
|
|
114
|
-
|
|
115
|
-
# Perform a recursive retrospection on the given current _node_
|
|
116
|
-
# on the given _path_.
|
|
117
|
-
def rec_introspect(node, path)
|
|
118
|
-
xml = bus.introspect_data(@name, path)
|
|
119
|
-
intfs, subnodes = IntrospectXMLParser.new(xml).parse
|
|
120
|
-
subnodes.each do |nodename|
|
|
121
|
-
subnode = node[nodename] = Node.new(nodename)
|
|
122
|
-
subpath = if path == "/"
|
|
123
|
-
"/" + nodename
|
|
124
|
-
else
|
|
125
|
-
path + "/" + nodename
|
|
126
|
-
end
|
|
127
|
-
rec_introspect(subnode, subpath)
|
|
128
|
-
end
|
|
129
|
-
return if intfs.empty?
|
|
130
|
-
node.object = ProxyObjectFactory.new(xml, @bus, @name, path).build
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# = Object path node class
|
|
135
|
-
#
|
|
136
|
-
# Class representing a node on an object path.
|
|
137
|
-
class Node < Hash
|
|
138
|
-
# The D-Bus object contained by the node.
|
|
139
|
-
attr_accessor :object
|
|
140
|
-
# The name of the node.
|
|
141
|
-
attr_reader :name
|
|
142
|
-
|
|
143
|
-
# Create a new node with a given _name_.
|
|
144
|
-
def initialize(name)
|
|
145
|
-
@name = name
|
|
146
|
-
@object = nil
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
# Return an XML string representation of the node.
|
|
150
|
-
# It is shallow, not recursing into subnodes
|
|
151
|
-
def to_xml
|
|
152
|
-
xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
|
153
|
-
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
154
|
-
<node>
|
|
155
|
-
'
|
|
156
|
-
each_pair do |k, _v|
|
|
157
|
-
xml += "<node name=\"#{k}\" />"
|
|
158
|
-
end
|
|
159
|
-
if @object
|
|
160
|
-
@object.intfs.each_pair do |_k, v|
|
|
161
|
-
xml += %(<interface name="#{v.name}">\n)
|
|
162
|
-
v.methods.each_value { |m| xml += m.to_xml }
|
|
163
|
-
v.signals.each_value { |m| xml += m.to_xml }
|
|
164
|
-
xml += "</interface>\n"
|
|
165
|
-
end
|
|
166
|
-
end
|
|
167
|
-
xml += "</node>"
|
|
168
|
-
xml
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Return inspect information of the node.
|
|
172
|
-
def inspect
|
|
173
|
-
# Need something here
|
|
174
|
-
"<DBus::Node #{sub_inspect}>"
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# Return instance inspect information, used by Node#inspect.
|
|
178
|
-
def sub_inspect
|
|
179
|
-
s = ""
|
|
180
|
-
if !@object.nil?
|
|
181
|
-
s += format("%x ", @object.object_id)
|
|
182
|
-
end
|
|
183
|
-
s + "{" + keys.collect { |k| "#{k} => #{self[k].sub_inspect}" }.join(",") + "}"
|
|
184
|
-
end
|
|
185
|
-
end # class Inspect
|
|
186
|
-
|
|
187
|
-
# FIXME: rename Connection to Bus?
|
|
188
|
-
|
|
189
|
-
# D-Bus main connection class
|
|
190
|
-
#
|
|
191
|
-
# Main class that maintains a connection to a bus and can handle incoming
|
|
192
|
-
# and outgoing messages.
|
|
193
|
-
class Connection
|
|
22
|
+
# A regular Bus {Connection}.
|
|
23
|
+
# As opposed to a peer connection to a single counterparty with no daemon in between.
|
|
24
|
+
class BusConnection < Connection
|
|
194
25
|
# The unique name (by specification) of the message.
|
|
195
26
|
attr_reader :unique_name
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
#
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
# and is something like:
|
|
203
|
-
# "transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2"
|
|
204
|
-
# e.g. "unix:path=/tmp/dbus-test" or "tcp:host=localhost,port=2687"
|
|
205
|
-
def initialize(path)
|
|
206
|
-
@message_queue = MessageQueue.new(path)
|
|
27
|
+
|
|
28
|
+
# Connect, authenticate, and send Hello.
|
|
29
|
+
# @param addresses [String]
|
|
30
|
+
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#addresses
|
|
31
|
+
def initialize(addresses)
|
|
32
|
+
super
|
|
207
33
|
@unique_name = nil
|
|
208
|
-
@method_call_replies = {}
|
|
209
|
-
@method_call_msgs = {}
|
|
210
|
-
@signal_matchrules = {}
|
|
211
34
|
@proxy = nil
|
|
212
|
-
|
|
213
|
-
end
|
|
214
|
-
|
|
215
|
-
# Dispatch all messages that are available in the queue,
|
|
216
|
-
# but do not block on the queue.
|
|
217
|
-
# Called by a main loop when something is available in the queue
|
|
218
|
-
def dispatch_message_queue
|
|
219
|
-
while (msg = @message_queue.pop(:non_block)) # FIXME: EOFError
|
|
220
|
-
process(msg)
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
# Tell a bus to register itself on the glib main loop
|
|
225
|
-
def glibize
|
|
226
|
-
require "glib2"
|
|
227
|
-
# Circumvent a ruby-glib bug
|
|
228
|
-
@channels ||= []
|
|
229
|
-
|
|
230
|
-
gio = GLib::IOChannel.new(@message_queue.socket.fileno)
|
|
231
|
-
@channels << gio
|
|
232
|
-
gio.add_watch(GLib::IOChannel::IN) do |_c, _ch|
|
|
233
|
-
dispatch_message_queue
|
|
234
|
-
true
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
# FIXME: describe the following names, flags and constants.
|
|
239
|
-
# See DBus spec for definition
|
|
240
|
-
NAME_FLAG_ALLOW_REPLACEMENT = 0x1
|
|
241
|
-
NAME_FLAG_REPLACE_EXISTING = 0x2
|
|
242
|
-
NAME_FLAG_DO_NOT_QUEUE = 0x4
|
|
243
|
-
|
|
244
|
-
REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x1
|
|
245
|
-
REQUEST_NAME_REPLY_IN_QUEUE = 0x2
|
|
246
|
-
REQUEST_NAME_REPLY_EXISTS = 0x3
|
|
247
|
-
REQUEST_NAME_REPLY_ALREADY_OWNER = 0x4
|
|
248
|
-
|
|
249
|
-
DBUSXMLINTRO = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
|
|
250
|
-
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
|
251
|
-
<node>
|
|
252
|
-
<interface name="org.freedesktop.DBus.Introspectable">
|
|
253
|
-
<method name="Introspect">
|
|
254
|
-
<arg name="data" direction="out" type="s"/>
|
|
255
|
-
</method>
|
|
256
|
-
</interface>
|
|
257
|
-
<interface name="org.freedesktop.DBus">
|
|
258
|
-
<method name="RequestName">
|
|
259
|
-
<arg direction="in" type="s"/>
|
|
260
|
-
<arg direction="in" type="u"/>
|
|
261
|
-
<arg direction="out" type="u"/>
|
|
262
|
-
</method>
|
|
263
|
-
<method name="ReleaseName">
|
|
264
|
-
<arg direction="in" type="s"/>
|
|
265
|
-
<arg direction="out" type="u"/>
|
|
266
|
-
</method>
|
|
267
|
-
<method name="StartServiceByName">
|
|
268
|
-
<arg direction="in" type="s"/>
|
|
269
|
-
<arg direction="in" type="u"/>
|
|
270
|
-
<arg direction="out" type="u"/>
|
|
271
|
-
</method>
|
|
272
|
-
<method name="Hello">
|
|
273
|
-
<arg direction="out" type="s"/>
|
|
274
|
-
</method>
|
|
275
|
-
<method name="NameHasOwner">
|
|
276
|
-
<arg direction="in" type="s"/>
|
|
277
|
-
<arg direction="out" type="b"/>
|
|
278
|
-
</method>
|
|
279
|
-
<method name="ListNames">
|
|
280
|
-
<arg direction="out" type="as"/>
|
|
281
|
-
</method>
|
|
282
|
-
<method name="ListActivatableNames">
|
|
283
|
-
<arg direction="out" type="as"/>
|
|
284
|
-
</method>
|
|
285
|
-
<method name="AddMatch">
|
|
286
|
-
<arg direction="in" type="s"/>
|
|
287
|
-
</method>
|
|
288
|
-
<method name="RemoveMatch">
|
|
289
|
-
<arg direction="in" type="s"/>
|
|
290
|
-
</method>
|
|
291
|
-
<method name="GetNameOwner">
|
|
292
|
-
<arg direction="in" type="s"/>
|
|
293
|
-
<arg direction="out" type="s"/>
|
|
294
|
-
</method>
|
|
295
|
-
<method name="ListQueuedOwners">
|
|
296
|
-
<arg direction="in" type="s"/>
|
|
297
|
-
<arg direction="out" type="as"/>
|
|
298
|
-
</method>
|
|
299
|
-
<method name="GetConnectionUnixUser">
|
|
300
|
-
<arg direction="in" type="s"/>
|
|
301
|
-
<arg direction="out" type="u"/>
|
|
302
|
-
</method>
|
|
303
|
-
<method name="GetConnectionUnixProcessID">
|
|
304
|
-
<arg direction="in" type="s"/>
|
|
305
|
-
<arg direction="out" type="u"/>
|
|
306
|
-
</method>
|
|
307
|
-
<method name="GetConnectionSELinuxSecurityContext">
|
|
308
|
-
<arg direction="in" type="s"/>
|
|
309
|
-
<arg direction="out" type="ay"/>
|
|
310
|
-
</method>
|
|
311
|
-
<method name="ReloadConfig">
|
|
312
|
-
</method>
|
|
313
|
-
<signal name="NameOwnerChanged">
|
|
314
|
-
<arg type="s"/>
|
|
315
|
-
<arg type="s"/>
|
|
316
|
-
<arg type="s"/>
|
|
317
|
-
</signal>
|
|
318
|
-
<signal name="NameLost">
|
|
319
|
-
<arg type="s"/>
|
|
320
|
-
</signal>
|
|
321
|
-
<signal name="NameAcquired">
|
|
322
|
-
<arg type="s"/>
|
|
323
|
-
</signal>
|
|
324
|
-
</interface>
|
|
325
|
-
</node>
|
|
326
|
-
'.freeze
|
|
327
|
-
# This apostroph is for syntax highlighting editors confused by above xml: "
|
|
328
|
-
|
|
329
|
-
# @api private
|
|
330
|
-
# Send a _message_.
|
|
331
|
-
# If _reply_handler_ is not given, wait for the reply
|
|
332
|
-
# and return the reply, or raise the error.
|
|
333
|
-
# If _reply_handler_ is given, it will be called when the reply
|
|
334
|
-
# eventually arrives, with the reply message as the 1st param
|
|
335
|
-
# and its params following
|
|
336
|
-
def send_sync_or_async(message, &reply_handler)
|
|
337
|
-
ret = nil
|
|
338
|
-
if reply_handler.nil?
|
|
339
|
-
send_sync(message) do |rmsg|
|
|
340
|
-
raise rmsg if rmsg.is_a?(Error)
|
|
341
|
-
ret = rmsg.params
|
|
342
|
-
end
|
|
343
|
-
else
|
|
344
|
-
on_return(message) do |rmsg|
|
|
345
|
-
if rmsg.is_a?(Error)
|
|
346
|
-
reply_handler.call(rmsg)
|
|
347
|
-
else
|
|
348
|
-
reply_handler.call(rmsg, * rmsg.params)
|
|
349
|
-
end
|
|
350
|
-
end
|
|
351
|
-
@message_queue.push(message)
|
|
352
|
-
end
|
|
353
|
-
ret
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
# @api private
|
|
357
|
-
def introspect_data(dest, path, &reply_handler)
|
|
358
|
-
m = DBus::Message.new(DBus::Message::METHOD_CALL)
|
|
359
|
-
m.path = path
|
|
360
|
-
m.interface = "org.freedesktop.DBus.Introspectable"
|
|
361
|
-
m.destination = dest
|
|
362
|
-
m.member = "Introspect"
|
|
363
|
-
m.sender = unique_name
|
|
364
|
-
if reply_handler.nil?
|
|
365
|
-
send_sync_or_async(m).first
|
|
366
|
-
else
|
|
367
|
-
send_sync_or_async(m) do |*args|
|
|
368
|
-
# TODO: test async introspection, is it used at all?
|
|
369
|
-
args.shift # forget the message, pass only the text
|
|
370
|
-
reply_handler.call(*args)
|
|
371
|
-
nil
|
|
372
|
-
end
|
|
373
|
-
end
|
|
374
|
-
end
|
|
375
|
-
|
|
376
|
-
# @api private
|
|
377
|
-
# Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
|
|
378
|
-
# _dest_ is the service and _path_ the object path you want to introspect
|
|
379
|
-
# If a code block is given, the introspect call in asynchronous. If not
|
|
380
|
-
# data is returned
|
|
381
|
-
#
|
|
382
|
-
# FIXME: link to ProxyObject data definition
|
|
383
|
-
# The returned object is a ProxyObject that has methods you can call to
|
|
384
|
-
# issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN
|
|
385
|
-
def introspect(dest, path)
|
|
386
|
-
if !block_given?
|
|
387
|
-
# introspect in synchronous !
|
|
388
|
-
data = introspect_data(dest, path)
|
|
389
|
-
pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
|
|
390
|
-
pof.build
|
|
391
|
-
else
|
|
392
|
-
introspect_data(dest, path) do |async_data|
|
|
393
|
-
yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build)
|
|
394
|
-
end
|
|
395
|
-
end
|
|
396
|
-
end
|
|
397
|
-
|
|
398
|
-
# Exception raised when a service name is requested that is not available.
|
|
399
|
-
class NameRequestError < Exception
|
|
400
|
-
end
|
|
401
|
-
|
|
402
|
-
# Attempt to request a service _name_.
|
|
403
|
-
#
|
|
404
|
-
# FIXME, NameRequestError cannot really be rescued as it will be raised
|
|
405
|
-
# when dispatching a later call. Rework the API to better match the spec.
|
|
406
|
-
# @return [Service]
|
|
407
|
-
def request_service(name)
|
|
408
|
-
# Use RequestName, but asynchronously!
|
|
409
|
-
# A synchronous call would not work with service activation, where
|
|
410
|
-
# method calls to be serviced arrive before the reply for RequestName
|
|
411
|
-
# (Ticket#29).
|
|
412
|
-
proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r|
|
|
413
|
-
# check and report errors first
|
|
414
|
-
raise rmsg if rmsg.is_a?(Error)
|
|
415
|
-
raise NameRequestError unless r == REQUEST_NAME_REPLY_PRIMARY_OWNER
|
|
416
|
-
end
|
|
417
|
-
@service = Service.new(name, self)
|
|
418
|
-
@service
|
|
35
|
+
send_hello
|
|
419
36
|
end
|
|
420
37
|
|
|
421
38
|
# Set up a ProxyObject for the bus itself, since the bus is introspectable.
|
|
@@ -424,10 +41,13 @@ module DBus
|
|
|
424
41
|
# Returns the object.
|
|
425
42
|
def proxy
|
|
426
43
|
if @proxy.nil?
|
|
44
|
+
xml_filename = File.expand_path("org.freedesktop.DBus.xml", __dir__)
|
|
45
|
+
xml = File.read(xml_filename)
|
|
46
|
+
|
|
427
47
|
path = "/org/freedesktop/DBus"
|
|
428
48
|
dest = "org.freedesktop.DBus"
|
|
429
49
|
pof = DBus::ProxyObjectFactory.new(
|
|
430
|
-
|
|
50
|
+
xml, self, dest, path,
|
|
431
51
|
api: ApiOptions::A0
|
|
432
52
|
)
|
|
433
53
|
@proxy = pof.build["org.freedesktop.DBus"]
|
|
@@ -435,151 +55,145 @@ module DBus
|
|
|
435
55
|
@proxy
|
|
436
56
|
end
|
|
437
57
|
|
|
438
|
-
#
|
|
439
|
-
#
|
|
440
|
-
|
|
441
|
-
|
|
58
|
+
# Request a well-known name so that clients can find us.
|
|
59
|
+
# @note Parameters other than *name* are advanced, you probably don't need them.
|
|
60
|
+
#
|
|
61
|
+
# With no boolean flags, running a second instance of a program that calls `request_name`
|
|
62
|
+
# will result in the second one failing, which this library translates to an exception.
|
|
63
|
+
# If you want the second instance to take over, you need both
|
|
64
|
+
# `allow_replacement: true` and `replace_existing: true.`
|
|
65
|
+
#
|
|
66
|
+
# @param name [BusName] the requested name
|
|
67
|
+
# @param replace_existing [Boolean]
|
|
68
|
+
# Replace an existing owner of the name, if that owner set *allow_replacement*.
|
|
69
|
+
# @param allow_replacement [Boolean]
|
|
70
|
+
# Other connections that specify *replace_existing* will be able to take
|
|
71
|
+
# the name from us. We will get {#on_name_lost NameLost}. If we specified *queue*
|
|
72
|
+
# we may get the name again, with {#on_name_acquired NameAcquired}.
|
|
73
|
+
# @param queue [Boolean]
|
|
74
|
+
# Affects the behavior when the bus denies the name (sooner or later).
|
|
75
|
+
# - If `false` (default), it is recommended to let the `NameRequestError` fall through and end your program.
|
|
76
|
+
# - If `true`, you should `rescue` the `NameRequestError` and set up
|
|
77
|
+
# {#on_name_acquired NameAcquired} and {#on_name_lost NameLost} handlers.
|
|
78
|
+
# Meanwhile, the bus will put us in a queue waiting for *name* (this is the "sooner" case).
|
|
79
|
+
# Also, if we had `allow_replacement: true`, another connection can cause us
|
|
80
|
+
# to lose the name. We will be moved back to the queue, waiting for when the other owners give up
|
|
81
|
+
# (the "later" case).
|
|
82
|
+
# @param flags [Integer,nil]
|
|
83
|
+
# If specified, overrides the boolean parameters.
|
|
84
|
+
# Use a bitwise sum `|` of:
|
|
85
|
+
# - NAME_FLAG_ALLOW_REPLACEMENT
|
|
86
|
+
# - NAME_FLAG_REPLACE_EXISTING
|
|
87
|
+
# - NAME_FLAG_DO_NOT_QUEUE
|
|
88
|
+
# Note that `0` implies `queue: true`.
|
|
89
|
+
#
|
|
90
|
+
# @return [REQUEST_NAME_REPLY_PRIMARY_OWNER,REQUEST_NAME_REPLY_ALREADY_OWNER] on success
|
|
91
|
+
# @raise [NameRequestError] with #error_code REQUEST_NAME_REPLY_EXISTS or REQUEST_NAME_REPLY_IN_QUEUE, on failure
|
|
92
|
+
# @raise DBus::Error another way to fail is being prohibited to own the name
|
|
93
|
+
# which is the default on the system bus
|
|
94
|
+
#
|
|
95
|
+
# @see https://dbus.freedesktop.org/doc/dbus-specification.html#bus-messages-request-name
|
|
96
|
+
#
|
|
97
|
+
# @example Simple usage
|
|
98
|
+
# bus = DBus.session_bus
|
|
99
|
+
# bus.object_server.export(DBus::Object.new("/org/example/Test"))
|
|
100
|
+
# bus.request_name("org.example.Test")
|
|
101
|
+
# # main loop
|
|
102
|
+
#
|
|
103
|
+
# @example Second instance taking over
|
|
104
|
+
# bus = DBus.session_bus
|
|
105
|
+
# bus.object_server.export(DBus::Object.new("/org/example/Test"))
|
|
106
|
+
# bus.on_name_lost { exit }
|
|
107
|
+
# bus.request_name("org.example.Test", allow_replacement: true, replace_existing: true)
|
|
108
|
+
# # main loop
|
|
109
|
+
#
|
|
110
|
+
# @example Second instance waiting for its turn
|
|
111
|
+
# bus = DBus.session_bus
|
|
112
|
+
# bus.object_server.export(DBus::Object.new("/org/example/Test"))
|
|
113
|
+
# bus.on_name_acquired { @owner = true }
|
|
114
|
+
# begin
|
|
115
|
+
# bus.request_name("org.example.Test", queue: true)
|
|
116
|
+
# rescue DBus::Connection::NameRequestError => e
|
|
117
|
+
# @owner = false
|
|
118
|
+
# end
|
|
119
|
+
# # main loop
|
|
120
|
+
def request_name(name,
|
|
121
|
+
allow_replacement: false,
|
|
122
|
+
replace_existing: false,
|
|
123
|
+
queue: false,
|
|
124
|
+
flags: nil)
|
|
125
|
+
if flags.nil?
|
|
126
|
+
flags = (allow_replacement ? NAME_FLAG_ALLOW_REPLACEMENT : 0) |
|
|
127
|
+
(replace_existing ? NAME_FLAG_REPLACE_EXISTING : 0) |
|
|
128
|
+
(queue ? 0 : NAME_FLAG_DO_NOT_QUEUE)
|
|
129
|
+
end
|
|
130
|
+
name = BusName.new(name)
|
|
131
|
+
r = proxy.RequestName(name, flags).first
|
|
132
|
+
handle_return_of_request_name(r, name)
|
|
442
133
|
end
|
|
443
134
|
|
|
444
|
-
#
|
|
445
|
-
#
|
|
446
|
-
#
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
retm = wait_for_message
|
|
459
|
-
process(retm)
|
|
460
|
-
end
|
|
461
|
-
rescue EOFError
|
|
462
|
-
new_err = DBus::Error.new("Connection dropped after we sent #{m.inspect}")
|
|
463
|
-
raise new_err
|
|
135
|
+
# The caller has released his claim on the given name.
|
|
136
|
+
# Either the caller was the primary owner of the name, and the name is now unused
|
|
137
|
+
# or taken by somebody waiting in the queue for the name,
|
|
138
|
+
# or the caller was waiting in the queue for the name and has now been removed from the queue.
|
|
139
|
+
RELEASE_NAME_REPLY_RELEASED = 1
|
|
140
|
+
# The given name does not exist on this bus.
|
|
141
|
+
RELEASE_NAME_REPLY_NON_EXISTENT = 2
|
|
142
|
+
# The caller was not the primary owner of this name, and was also not waiting in the queue to own this name.
|
|
143
|
+
RELEASE_NAME_REPLY_NOT_OWNER = 3
|
|
144
|
+
|
|
145
|
+
# @param name [BusName] the name to release
|
|
146
|
+
def release_name(name)
|
|
147
|
+
name = BusName.new(name)
|
|
148
|
+
proxy.ReleaseName(name).first
|
|
464
149
|
end
|
|
465
150
|
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
raise "on_return should only get method_calls"
|
|
473
|
-
end
|
|
474
|
-
@method_call_msgs[m.serial] = m
|
|
475
|
-
@method_call_replies[m.serial] = retc
|
|
151
|
+
def on_name_acquired(&handler)
|
|
152
|
+
proxy.on_signal("NameAcquired", &handler)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def on_name_lost(&handler)
|
|
156
|
+
proxy.on_signal("NameLost", &handler)
|
|
476
157
|
end
|
|
477
158
|
|
|
478
159
|
# Asks bus to send us messages matching mr, and execute slot when
|
|
479
160
|
# received
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
161
|
+
# @param match_rule [MatchRule,#to_s]
|
|
162
|
+
# @return [void]
|
|
163
|
+
def add_match(match_rule, &slot)
|
|
164
|
+
mrs = match_rule.to_s
|
|
165
|
+
rule_existed = super(mrs, &slot)
|
|
484
166
|
# don't ask for the same match if we override it
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
@signal_matchrules[mrs] = slot
|
|
167
|
+
return if rule_existed
|
|
168
|
+
|
|
169
|
+
DBus.logger.debug "Asked for a new match"
|
|
170
|
+
proxy.AddMatch(mrs)
|
|
490
171
|
end
|
|
491
172
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
173
|
+
# @param match_rule [MatchRule,#to_s]
|
|
174
|
+
# @return [void]
|
|
175
|
+
def remove_match(match_rule)
|
|
176
|
+
mrs = match_rule.to_s
|
|
177
|
+
rule_existed = super(mrs)
|
|
495
178
|
# don't remove nonexisting matches.
|
|
496
179
|
return if rule_existed
|
|
180
|
+
|
|
497
181
|
# FIXME: if we do try, the Error.MatchRuleNotFound is *not* raised
|
|
498
182
|
# and instead is reported as "no return code for nil"
|
|
499
183
|
proxy.RemoveMatch(mrs)
|
|
500
184
|
end
|
|
501
185
|
|
|
502
|
-
#
|
|
503
|
-
#
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
case m.message_type
|
|
507
|
-
when Message::ERROR, Message::METHOD_RETURN
|
|
508
|
-
raise InvalidPacketException if m.reply_serial.nil?
|
|
509
|
-
mcs = @method_call_replies[m.reply_serial]
|
|
510
|
-
if !mcs
|
|
511
|
-
DBus.logger.debug "no return code for mcs: #{mcs.inspect} m: #{m.inspect}"
|
|
512
|
-
else
|
|
513
|
-
if m.message_type == Message::ERROR
|
|
514
|
-
mcs.call(Error.new(m))
|
|
515
|
-
else
|
|
516
|
-
mcs.call(m)
|
|
517
|
-
end
|
|
518
|
-
@method_call_replies.delete(m.reply_serial)
|
|
519
|
-
@method_call_msgs.delete(m.reply_serial)
|
|
520
|
-
end
|
|
521
|
-
when DBus::Message::METHOD_CALL
|
|
522
|
-
if m.path == "/org/freedesktop/DBus"
|
|
523
|
-
DBus.logger.debug "Got method call on /org/freedesktop/DBus"
|
|
524
|
-
end
|
|
525
|
-
node = @service.get_node(m.path)
|
|
526
|
-
if !node
|
|
527
|
-
reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject",
|
|
528
|
-
"Object #{m.path} doesn't exist")
|
|
529
|
-
@message_queue.push(reply)
|
|
530
|
-
# handle introspectable as an exception:
|
|
531
|
-
elsif m.interface == "org.freedesktop.DBus.Introspectable" &&
|
|
532
|
-
m.member == "Introspect"
|
|
533
|
-
reply = Message.new(Message::METHOD_RETURN).reply_to(m)
|
|
534
|
-
reply.sender = @unique_name
|
|
535
|
-
reply.add_param(Type::STRING, node.to_xml)
|
|
536
|
-
@message_queue.push(reply)
|
|
537
|
-
else
|
|
538
|
-
obj = node.object
|
|
539
|
-
return if obj.nil? # FIXME, pushes no reply
|
|
540
|
-
obj.dispatch(m) if obj
|
|
541
|
-
end
|
|
542
|
-
when DBus::Message::SIGNAL
|
|
543
|
-
# the signal can match multiple different rules
|
|
544
|
-
# clone to allow new signale handlers to be registered
|
|
545
|
-
@signal_matchrules.dup.each do |mrs, slot|
|
|
546
|
-
if DBus::MatchRule.new.from_s(mrs).match(m)
|
|
547
|
-
slot.call(m)
|
|
548
|
-
end
|
|
549
|
-
end
|
|
550
|
-
else
|
|
551
|
-
DBus.logger.debug "Unknown message type: #{m.message_type}"
|
|
552
|
-
end
|
|
553
|
-
rescue Exception => ex
|
|
554
|
-
raise m.annotate_exception(ex)
|
|
555
|
-
end
|
|
556
|
-
|
|
557
|
-
# Retrieves the Service with the given _name_.
|
|
558
|
-
# @return [Service]
|
|
186
|
+
# Makes a {ProxyService} with the given *name*.
|
|
187
|
+
# Note that this succeeds even if the name does not exist and cannot be
|
|
188
|
+
# activated. It will only fail when calling a method.
|
|
189
|
+
# @return [ProxyService]
|
|
559
190
|
def service(name)
|
|
560
191
|
# The service might not exist at this time so we cannot really check
|
|
561
192
|
# anything
|
|
562
|
-
|
|
193
|
+
ProxyService.new(name, self)
|
|
563
194
|
end
|
|
564
195
|
alias [] service
|
|
565
196
|
|
|
566
|
-
# @api private
|
|
567
|
-
# Emit a signal event for the given _service_, object _obj_, interface
|
|
568
|
-
# _intf_ and signal _sig_ with arguments _args_.
|
|
569
|
-
def emit(service, obj, intf, sig, *args)
|
|
570
|
-
m = Message.new(DBus::Message::SIGNAL)
|
|
571
|
-
m.path = obj.path
|
|
572
|
-
m.interface = intf.name
|
|
573
|
-
m.member = sig.name
|
|
574
|
-
m.sender = service.name
|
|
575
|
-
i = 0
|
|
576
|
-
sig.params.each do |par|
|
|
577
|
-
m.add_param(par.type, args[i])
|
|
578
|
-
i += 1
|
|
579
|
-
end
|
|
580
|
-
@message_queue.push(m)
|
|
581
|
-
end
|
|
582
|
-
|
|
583
197
|
###########################################################################
|
|
584
198
|
private
|
|
585
199
|
|
|
@@ -594,9 +208,8 @@ module DBus
|
|
|
594
208
|
@unique_name = rmsg.destination
|
|
595
209
|
DBus.logger.debug "Got hello reply. Our unique_name is #{@unique_name}"
|
|
596
210
|
end
|
|
597
|
-
@service = Service.new(@unique_name, self)
|
|
598
211
|
end
|
|
599
|
-
end
|
|
212
|
+
end
|
|
600
213
|
|
|
601
214
|
# = D-Bus session bus class
|
|
602
215
|
#
|
|
@@ -604,17 +217,17 @@ module DBus
|
|
|
604
217
|
#
|
|
605
218
|
# Use SessionBus, the non-singleton ASessionBus is
|
|
606
219
|
# for the test suite.
|
|
607
|
-
class ASessionBus <
|
|
220
|
+
class ASessionBus < BusConnection
|
|
608
221
|
# Get the the default session bus.
|
|
609
222
|
def initialize
|
|
610
223
|
super(self.class.session_bus_address)
|
|
611
|
-
send_hello
|
|
612
224
|
end
|
|
613
225
|
|
|
614
226
|
def self.session_bus_address
|
|
615
227
|
ENV["DBUS_SESSION_BUS_ADDRESS"] ||
|
|
616
228
|
address_from_file ||
|
|
617
|
-
"launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET"
|
|
229
|
+
("launchd:env=DBUS_LAUNCHD_SESSION_BUS_SOCKET" if Platform.macos?) ||
|
|
230
|
+
(raise NotImplementedError, "Cannot find session bus; sorry, haven't figured out autolaunch yet")
|
|
618
231
|
end
|
|
619
232
|
|
|
620
233
|
def self.address_from_file
|
|
@@ -622,6 +235,7 @@ module DBus
|
|
|
622
235
|
# traditional dbus uses /var/lib/dbus/machine-id
|
|
623
236
|
machine_id_path = Dir["{/etc,/var/lib/dbus,/var/db/dbus}/machine-id"].first
|
|
624
237
|
return nil unless machine_id_path
|
|
238
|
+
|
|
625
239
|
machine_id = File.read(machine_id_path).chomp
|
|
626
240
|
|
|
627
241
|
display = ENV["DISPLAY"][/:(\d+)\.?/, 1]
|
|
@@ -643,6 +257,9 @@ module DBus
|
|
|
643
257
|
include Singleton
|
|
644
258
|
end
|
|
645
259
|
|
|
260
|
+
# Default socket name for the system bus.
|
|
261
|
+
SYSTEM_BUS_ADDRESS = "unix:path=/var/run/dbus/system_bus_socket"
|
|
262
|
+
|
|
646
263
|
# = D-Bus system bus class
|
|
647
264
|
#
|
|
648
265
|
# The system bus is a system-wide bus mostly used for global or
|
|
@@ -650,11 +267,14 @@ module DBus
|
|
|
650
267
|
#
|
|
651
268
|
# Use SystemBus, the non-singleton ASystemBus is
|
|
652
269
|
# for the test suite.
|
|
653
|
-
class ASystemBus <
|
|
270
|
+
class ASystemBus < BusConnection
|
|
654
271
|
# Get the default system bus.
|
|
655
272
|
def initialize
|
|
656
|
-
super(
|
|
657
|
-
|
|
273
|
+
super(self.class.system_bus_address)
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
def self.system_bus_address
|
|
277
|
+
ENV["DBUS_SYSTEM_BUS_ADDRESS"] || SYSTEM_BUS_ADDRESS
|
|
658
278
|
end
|
|
659
279
|
end
|
|
660
280
|
|
|
@@ -668,13 +288,10 @@ module DBus
|
|
|
668
288
|
# (for Unix-socket) unix:path=/tmp/my_funky_bus_socket
|
|
669
289
|
#
|
|
670
290
|
# you'll need to take care about authentification then, more info here:
|
|
671
|
-
#
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
super(socket_name)
|
|
676
|
-
send_hello
|
|
677
|
-
end
|
|
291
|
+
# https://gitlab.com/pangdudu/ruby-dbus/-/blob/master/README.rdoc
|
|
292
|
+
# TODO: keep the name but update the docs
|
|
293
|
+
# @deprecated just use BusConnection
|
|
294
|
+
class RemoteBus < BusConnection
|
|
678
295
|
end
|
|
679
296
|
|
|
680
297
|
# See ASystemBus
|
|
@@ -683,64 +300,14 @@ module DBus
|
|
|
683
300
|
end
|
|
684
301
|
|
|
685
302
|
# Shortcut for the {SystemBus} instance
|
|
686
|
-
# @return [
|
|
303
|
+
# @return [BusConnection]
|
|
687
304
|
def self.system_bus
|
|
688
305
|
SystemBus.instance
|
|
689
306
|
end
|
|
690
307
|
|
|
691
308
|
# Shortcut for the {SessionBus} instance
|
|
692
|
-
# @return [
|
|
309
|
+
# @return [BusConnection]
|
|
693
310
|
def self.session_bus
|
|
694
311
|
SessionBus.instance
|
|
695
312
|
end
|
|
696
|
-
|
|
697
|
-
# = Main event loop class.
|
|
698
|
-
#
|
|
699
|
-
# Class that takes care of handling message and signal events
|
|
700
|
-
# asynchronously. *Note:* This is a native implement and therefore does
|
|
701
|
-
# not integrate with a graphical widget set main loop.
|
|
702
|
-
class Main
|
|
703
|
-
# Create a new main event loop.
|
|
704
|
-
def initialize
|
|
705
|
-
@buses = {}
|
|
706
|
-
@quitting = false
|
|
707
|
-
end
|
|
708
|
-
|
|
709
|
-
# Add a _bus_ to the list of buses to watch for events.
|
|
710
|
-
def <<(bus)
|
|
711
|
-
@buses[bus.message_queue.socket] = bus
|
|
712
|
-
end
|
|
713
|
-
|
|
714
|
-
# Quit a running main loop, to be used eg. from a signal handler
|
|
715
|
-
def quit
|
|
716
|
-
@quitting = true
|
|
717
|
-
end
|
|
718
|
-
|
|
719
|
-
# Run the main loop. This is a blocking call!
|
|
720
|
-
def run
|
|
721
|
-
# before blocking, empty the buffers
|
|
722
|
-
# https://bugzilla.novell.com/show_bug.cgi?id=537401
|
|
723
|
-
@buses.each_value do |b|
|
|
724
|
-
while (m = b.message_queue.message_from_buffer_nonblock)
|
|
725
|
-
b.process(m)
|
|
726
|
-
end
|
|
727
|
-
end
|
|
728
|
-
while !@quitting && !@buses.empty?
|
|
729
|
-
ready = IO.select(@buses.keys, [], [], 5) # timeout 5 seconds
|
|
730
|
-
next unless ready # timeout exceeds so continue unless quitting
|
|
731
|
-
ready.first.each do |socket|
|
|
732
|
-
b = @buses[socket]
|
|
733
|
-
begin
|
|
734
|
-
b.message_queue.buffer_from_socket_nonblock
|
|
735
|
-
rescue EOFError, SystemCallError
|
|
736
|
-
@buses.delete socket # this bus died
|
|
737
|
-
next
|
|
738
|
-
end
|
|
739
|
-
while (m = b.message_queue.message_from_buffer_nonblock)
|
|
740
|
-
b.process(m)
|
|
741
|
-
end
|
|
742
|
-
end
|
|
743
|
-
end
|
|
744
|
-
end
|
|
745
|
-
end # class Main
|
|
746
|
-
end # module DBus
|
|
313
|
+
end
|