ruby-dbus 0.23.0.beta1 → 0.23.0.beta2

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.
@@ -0,0 +1,350 @@
1
+ # frozen_string_literal: true
2
+
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ # Copyright (C) 2023 Martin Vidner
6
+ #
7
+ # This library is free software; you can redistribute it and/or
8
+ # modify it under the terms of the GNU Lesser General Public
9
+ # License, version 2.1 as published by the Free Software Foundation.
10
+ # See the file "COPYING" for the exact licensing terms.
11
+
12
+ module DBus
13
+ # D-Bus main connection class
14
+ #
15
+ # Main class that maintains a connection to a bus and can handle incoming
16
+ # and outgoing messages.
17
+ class Connection
18
+ # pop and push messages here
19
+ # @return [MessageQueue]
20
+ attr_reader :message_queue
21
+
22
+ # Create a new connection to the bus for a given connect _path_. _path_
23
+ # format is described in the D-Bus specification:
24
+ # http://dbus.freedesktop.org/doc/dbus-specification.html#addresses
25
+ # and is something like:
26
+ # "transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2"
27
+ # e.g. "unix:path=/tmp/dbus-test" or "tcp:host=localhost,port=2687"
28
+ def initialize(path)
29
+ @message_queue = MessageQueue.new(path)
30
+
31
+ # @return [Hash{Integer => Proc}]
32
+ # key: message serial
33
+ # value: block to be run when the reply to that message is received
34
+ @method_call_replies = {}
35
+
36
+ # @return [Hash{Integer => Message}]
37
+ # for debugging only: messages for which a reply was not received yet;
38
+ # key == value.serial
39
+ @method_call_msgs = {}
40
+ @signal_matchrules = {}
41
+ end
42
+
43
+ def object_server
44
+ @object_server ||= ObjectServer.new(self)
45
+ end
46
+
47
+ # Dispatch all messages that are available in the queue,
48
+ # but do not block on the queue.
49
+ # Called by a main loop when something is available in the queue
50
+ def dispatch_message_queue
51
+ while (msg = @message_queue.pop(blocking: false)) # FIXME: EOFError
52
+ process(msg)
53
+ end
54
+ end
55
+
56
+ # Tell a bus to register itself on the glib main loop
57
+ def glibize
58
+ require "glib2"
59
+ # Circumvent a ruby-glib bug
60
+ @channels ||= []
61
+
62
+ gio = GLib::IOChannel.new(@message_queue.socket.fileno)
63
+ @channels << gio
64
+ gio.add_watch(GLib::IOChannel::IN) do |_c, _ch|
65
+ dispatch_message_queue
66
+ true
67
+ end
68
+ end
69
+
70
+ # NAME_FLAG_* and REQUEST_NAME_* belong to BusConnection
71
+ # but users will have referenced them in Connection so they need to stay here
72
+
73
+ # FIXME: describe the following names, flags and constants.
74
+ # See DBus spec for definition
75
+ NAME_FLAG_ALLOW_REPLACEMENT = 0x1
76
+ NAME_FLAG_REPLACE_EXISTING = 0x2
77
+ NAME_FLAG_DO_NOT_QUEUE = 0x4
78
+
79
+ REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x1
80
+ REQUEST_NAME_REPLY_IN_QUEUE = 0x2
81
+ REQUEST_NAME_REPLY_EXISTS = 0x3
82
+ REQUEST_NAME_REPLY_ALREADY_OWNER = 0x4
83
+
84
+ # @api private
85
+ # Send a _message_.
86
+ # If _reply_handler_ is not given, wait for the reply
87
+ # and return the reply, or raise the error.
88
+ # If _reply_handler_ is given, it will be called when the reply
89
+ # eventually arrives, with the reply message as the 1st param
90
+ # and its params following
91
+ def send_sync_or_async(message, &reply_handler)
92
+ ret = nil
93
+ if reply_handler.nil?
94
+ send_sync(message) do |rmsg|
95
+ raise rmsg if rmsg.is_a?(Error)
96
+
97
+ ret = rmsg.params
98
+ end
99
+ else
100
+ on_return(message) do |rmsg|
101
+ if rmsg.is_a?(Error)
102
+ reply_handler.call(rmsg)
103
+ else
104
+ reply_handler.call(rmsg, * rmsg.params)
105
+ end
106
+ end
107
+ @message_queue.push(message)
108
+ end
109
+ ret
110
+ end
111
+
112
+ # @api private
113
+ def introspect_data(dest, path, &reply_handler)
114
+ m = DBus::Message.new(DBus::Message::METHOD_CALL)
115
+ m.path = path
116
+ m.interface = "org.freedesktop.DBus.Introspectable"
117
+ m.destination = dest
118
+ m.member = "Introspect"
119
+ m.sender = unique_name
120
+ if reply_handler.nil?
121
+ send_sync_or_async(m).first
122
+ else
123
+ send_sync_or_async(m) do |*args|
124
+ # TODO: test async introspection, is it used at all?
125
+ args.shift # forget the message, pass only the text
126
+ reply_handler.call(*args)
127
+ nil
128
+ end
129
+ end
130
+ end
131
+
132
+ # @api private
133
+ # Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
134
+ # _dest_ is the service and _path_ the object path you want to introspect
135
+ # If a code block is given, the introspect call in asynchronous. If not
136
+ # data is returned
137
+ #
138
+ # FIXME: link to ProxyObject data definition
139
+ # The returned object is a ProxyObject that has methods you can call to
140
+ # issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN
141
+ def introspect(dest, path)
142
+ if !block_given?
143
+ # introspect in synchronous !
144
+ data = introspect_data(dest, path)
145
+ pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
146
+ pof.build
147
+ else
148
+ introspect_data(dest, path) do |async_data|
149
+ yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build)
150
+ end
151
+ end
152
+ end
153
+
154
+ # Exception raised when a service name is requested that is not available.
155
+ class NameRequestError < Exception
156
+ end
157
+
158
+ def handle_return_of_request_name(ret, name)
159
+ details = if ret == REQUEST_NAME_REPLY_IN_QUEUE
160
+ other = proxy.GetNameOwner(name).first
161
+ other_creds = proxy.GetConnectionCredentials(other).first
162
+ "already owned by #{other}, #{other_creds.inspect}"
163
+ else
164
+ "error code #{ret}"
165
+ end
166
+ raise NameRequestError, "Could not request #{name}, #{details}" unless ret == REQUEST_NAME_REPLY_PRIMARY_OWNER
167
+
168
+ ret
169
+ end
170
+
171
+ # Attempt to request a service _name_.
172
+ # @raise NameRequestError which cannot really be rescued as it will be raised when dispatching a later call.
173
+ # @return [ObjectServer]
174
+ # @deprecated Use {BusConnection#request_name}.
175
+ def request_service(name)
176
+ # Use RequestName, but asynchronously!
177
+ # A synchronous call would not work with service activation, where
178
+ # method calls to be serviced arrive before the reply for RequestName
179
+ # (Ticket#29).
180
+ proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r|
181
+ # check and report errors first
182
+ raise rmsg if rmsg.is_a?(Error)
183
+
184
+ handle_return_of_request_name(r, name)
185
+ end
186
+ object_server
187
+ end
188
+
189
+ # @api private
190
+ # Wait for a message to arrive. Return it once it is available.
191
+ def wait_for_message
192
+ @message_queue.pop # FIXME: EOFError
193
+ end
194
+
195
+ # @api private
196
+ # Send a message _msg_ on to the bus. This is done synchronously, thus
197
+ # the call will block until a reply message arrives.
198
+ # @param msg [Message]
199
+ # @param retc [Proc] the reply handler
200
+ # @yieldparam rmsg [MethodReturnMessage] the reply
201
+ # @yieldreturn [Array<Object>] the reply (out) parameters
202
+ def send_sync(msg, &retc) # :yields: reply/return message
203
+ return if msg.nil? # check if somethings wrong
204
+
205
+ @message_queue.push(msg)
206
+ @method_call_msgs[msg.serial] = msg
207
+ @method_call_replies[msg.serial] = retc
208
+
209
+ retm = wait_for_message
210
+ return if retm.nil? # check if somethings wrong
211
+
212
+ process(retm)
213
+ while @method_call_replies.key? msg.serial
214
+ retm = wait_for_message
215
+ process(retm)
216
+ end
217
+ rescue EOFError
218
+ new_err = DBus::Error.new("Connection dropped after we sent #{msg.inspect}")
219
+ raise new_err
220
+ end
221
+
222
+ # @api private
223
+ # Specify a code block that has to be executed when a reply for
224
+ # message _msg_ is received.
225
+ # @param msg [Message]
226
+ def on_return(msg, &retc)
227
+ # Have a better exception here
228
+ if msg.message_type != Message::METHOD_CALL
229
+ raise "on_return should only get method_calls"
230
+ end
231
+
232
+ @method_call_msgs[msg.serial] = msg
233
+ @method_call_replies[msg.serial] = retc
234
+ end
235
+
236
+ # Asks bus to send us messages matching mr, and execute slot when
237
+ # received
238
+ # @param match_rule [MatchRule,#to_s]
239
+ # @return [void] actually return whether the rule existed, internal detail
240
+ def add_match(match_rule, &slot)
241
+ # check this is a signal.
242
+ mrs = match_rule.to_s
243
+ DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}"
244
+ rule_existed = @signal_matchrules.key?(mrs)
245
+ @signal_matchrules[mrs] = slot
246
+ rule_existed
247
+ end
248
+
249
+ # @param match_rule [MatchRule,#to_s]
250
+ # @return [void] actually return whether the rule existed, internal detail
251
+ def remove_match(match_rule)
252
+ mrs = match_rule.to_s
253
+ @signal_matchrules.delete(mrs).nil?
254
+ end
255
+
256
+ # @api private
257
+ # Process a message _msg_ based on its type.
258
+ # @param msg [Message]
259
+ def process(msg)
260
+ return if msg.nil? # check if somethings wrong
261
+
262
+ case msg.message_type
263
+ when Message::ERROR, Message::METHOD_RETURN
264
+ raise InvalidPacketException if msg.reply_serial.nil?
265
+
266
+ mcs = @method_call_replies[msg.reply_serial]
267
+ if !mcs
268
+ DBus.logger.debug "no return code for mcs: #{mcs.inspect} msg: #{msg.inspect}"
269
+ else
270
+ if msg.message_type == Message::ERROR
271
+ mcs.call(Error.new(msg))
272
+ else
273
+ mcs.call(msg)
274
+ end
275
+ @method_call_replies.delete(msg.reply_serial)
276
+ @method_call_msgs.delete(msg.reply_serial)
277
+ end
278
+ when DBus::Message::METHOD_CALL
279
+ if msg.path == "/org/freedesktop/DBus"
280
+ DBus.logger.debug "Got method call on /org/freedesktop/DBus"
281
+ end
282
+ node = object_server.get_node(msg.path, create: false)
283
+ # introspect a known path even if there is no object on it
284
+ if node &&
285
+ msg.interface == "org.freedesktop.DBus.Introspectable" &&
286
+ msg.member == "Introspect"
287
+ reply = Message.new(Message::METHOD_RETURN).reply_to(msg)
288
+ reply.sender = @unique_name
289
+ xml = node.to_xml(msg.path)
290
+ reply.add_param(Type::STRING, xml)
291
+ @message_queue.push(reply)
292
+ # dispatch for an object
293
+ elsif node&.object
294
+ node.object.dispatch(msg)
295
+ else
296
+ reply = Message.error(msg, "org.freedesktop.DBus.Error.UnknownObject",
297
+ "Object #{msg.path} doesn't exist")
298
+ @message_queue.push(reply)
299
+ end
300
+ when DBus::Message::SIGNAL
301
+ # the signal can match multiple different rules
302
+ # clone to allow new signale handlers to be registered
303
+ @signal_matchrules.dup.each do |mrs, slot|
304
+ if DBus::MatchRule.new.from_s(mrs).match(msg)
305
+ slot.call(msg)
306
+ end
307
+ end
308
+ else
309
+ # spec(Message Format): Unknown types must be ignored.
310
+ DBus.logger.debug "Unknown message type: #{msg.message_type}"
311
+ end
312
+ rescue Exception => e
313
+ raise msg.annotate_exception(e)
314
+ end
315
+
316
+ # @api private
317
+ # Emit a signal event for the given _service_, object _obj_, interface
318
+ # _intf_ and signal _sig_ with arguments _args_.
319
+ # @param _service unused
320
+ # @param obj [DBus::Object]
321
+ # @param intf [Interface]
322
+ # @param sig [Signal]
323
+ # @param args arguments for the signal
324
+ def emit(_service, obj, intf, sig, *args)
325
+ m = Message.new(DBus::Message::SIGNAL)
326
+ m.path = obj.path
327
+ m.interface = intf.name
328
+ m.member = sig.name
329
+ i = 0
330
+ sig.params.each do |par|
331
+ m.add_param(par.type, args[i])
332
+ i += 1
333
+ end
334
+ @message_queue.push(m)
335
+ end
336
+ end
337
+
338
+ # A {Connection} that is talking directly to a peer, with no bus daemon in between.
339
+ # A prominent example is the PulseAudio connection,
340
+ # see https://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/DBus/
341
+ # When starting, it still starts with authentication but omits the Hello message.
342
+ class PeerConnection < Connection
343
+ # Get a {ProxyPeerService}, a dummy helper to get {ProxyObject}s for
344
+ # a {PeerConnection}.
345
+ # @return [ProxyPeerService]
346
+ def peer_service
347
+ ProxyPeerService.new(self)
348
+ end
349
+ end
350
+ end
data/lib/dbus/object.rb CHANGED
@@ -19,31 +19,37 @@ module DBus
19
19
  # Objects that are going to be exported by a D-Bus service
20
20
  # should inherit from this class. At the client side, use {ProxyObject}.
21
21
  class Object
22
- # The path of the object.
22
+ # @return [ObjectPath] The path of the object.
23
23
  attr_reader :path
24
24
 
25
25
  # The interfaces that the object supports. Hash: String => Interface
26
26
  my_class_attribute :intfs
27
27
  self.intfs = {}
28
28
 
29
- # @return [Connection] the connection the object is exported by
30
- attr_reader :connection
31
-
32
29
  @@cur_intf = nil # Interface
33
30
  @@intfs_mutex = Mutex.new
34
31
 
35
32
  # Create a new object with a given _path_.
36
33
  # Use ObjectServer#export to export it.
34
+ # @param path [ObjectPath] The path of the object.
37
35
  def initialize(path)
38
36
  @path = path
39
- @connection = nil
37
+ # TODO: what parts of our API are supposed to work before we're exported?
38
+ self.object_server = nil
39
+ end
40
+
41
+ # @return [ObjectServer] the server the object is exported by
42
+ def object_server
43
+ # tests may mock the old ivar
44
+ @object_server || @service
40
45
  end
41
46
 
42
- # @param connection [Connection] the connection the object is exported by
43
- def connection=(connection)
44
- @connection = connection
45
- # deprecated, keeping @service for compatibility
46
- @service = connection&.object_server
47
+ # @param server [ObjectServer] the server the object is exported by
48
+ # @note only the server itself should call this in its #export/#unexport
49
+ def object_server=(server)
50
+ # until v0.22.1 there was attr_writer :service
51
+ # so subclasses only could use @service
52
+ @object_server = @service = server
47
53
  end
48
54
 
49
55
  # Dispatch a message _msg_ to call exported methods
@@ -78,7 +84,9 @@ module DBus
78
84
  dbus_msg_exc = msg.annotate_exception(e)
79
85
  reply = ErrorMessage.from_exception(dbus_msg_exc).reply_to(msg)
80
86
  end
81
- @connection.message_queue.push(reply)
87
+ # TODO: this method chain is too long,
88
+ # we should probably just return reply [Message] like we get a [Message]
89
+ object_server.connection.message_queue.push(reply)
82
90
  end
83
91
  end
84
92
 
@@ -316,7 +324,9 @@ module DBus
316
324
  # @param sig [Signal]
317
325
  # @param args arguments for the signal
318
326
  def emit(intf, sig, *args)
319
- @connection.emit(nil, self, intf, sig, *args)
327
+ raise "Cannot emit signal #{intf.name}.#{sig.name} before #{path} is exported" if object_server.nil?
328
+
329
+ object_server.connection.emit(nil, self, intf, sig, *args)
320
330
  end
321
331
 
322
332
  # Defines a signal for the object with a given name _sym_ and _prototype_.
@@ -20,27 +20,31 @@ module DBus
20
20
  module ObjectManager
21
21
  OBJECT_MANAGER_INTERFACE = "org.freedesktop.DBus.ObjectManager"
22
22
 
23
+ # Implements `the GetManagedObjects` method.
23
24
  # @return [Hash{ObjectPath => Hash{String => Hash{String => Data::Base}}}]
24
25
  # object -> interface -> property -> value
25
26
  def managed_objects
26
- descendant_objects = connection.object_server.descendants_for(path)
27
+ descendant_objects = object_server.descendants_for(path)
27
28
  descendant_objects.each_with_object({}) do |obj, hash|
28
29
  hash[obj.path] = obj.interfaces_and_properties
29
30
  end
30
31
  end
31
32
 
33
+ # {ObjectServer#export} will call this for you to emit the `InterfacesAdded` signal.
32
34
  # @param object [DBus::Object]
33
35
  # @return [void]
34
36
  def object_added(object)
35
37
  InterfacesAdded(object.path, object.interfaces_and_properties)
36
38
  end
37
39
 
40
+ # {ObjectServer#unexport} will call this for you to emit the `InterfacesRemoved` signal.
38
41
  # @param object [DBus::Object]
39
42
  # @return [void]
40
43
  def object_removed(object)
41
44
  InterfacesRemoved(object.path, object.intfs.keys)
42
45
  end
43
46
 
47
+ # Module#included, a hook for `include ObjectManager`, declares its dbus_interface.
44
48
  def self.included(base)
45
49
  base.class_eval do
46
50
  dbus_interface OBJECT_MANAGER_INTERFACE do
@@ -36,7 +36,7 @@ module DBus
36
36
 
37
37
  # Retrieves an object at the given _path_
38
38
  # @param path [ObjectPath]
39
- # @return [DBus::Object]
39
+ # @return [DBus::Object,nil]
40
40
  def object(path)
41
41
  node = get_node(path, create: false)
42
42
  node&.object
@@ -45,34 +45,48 @@ module DBus
45
45
 
46
46
  # Export an object
47
47
  # @param obj [DBus::Object]
48
+ # @raise RuntimeError if there's already an exported object at the same path
48
49
  def export(obj)
49
50
  node = get_node(obj.path, create: true)
50
- # TODO: clarify that this is indeed the right thing, and document
51
- # raise "At #{obj.path} there is already an object #{node.object.inspect}" if node.object
51
+ raise "At #{obj.path} there is already an object #{node.object.inspect}" if node.object
52
52
 
53
53
  node.object = obj
54
54
 
55
- obj.connection = @connection
55
+ obj.object_server = self
56
56
  object_manager_for(obj)&.object_added(obj)
57
57
  end
58
58
 
59
- # Undo exporting an object _obj_.
59
+ # Undo exporting an object *obj_or_path*.
60
60
  # Raises ArgumentError if it is not a DBus::Object.
61
61
  # Returns the object, or false if _obj_ was not exported.
62
- # @param obj [DBus::Object]
63
- def unexport(obj)
64
- raise ArgumentError, "Expecting a DBus::Object argument" unless obj.is_a?(DBus::Object)
65
-
66
- last_path_separator_idx = obj.path.rindex("/")
67
- parent_path = obj.path[1..last_path_separator_idx - 1]
68
- node_name = obj.path[last_path_separator_idx + 1..-1]
62
+ # @param obj_or_path [DBus::Object,ObjectPath,String] an object or a valid object path
63
+ def unexport(obj_or_path)
64
+ path = self.class.path_of(obj_or_path)
65
+ parent_path, _separator, node_name = path.rpartition("/")
69
66
 
70
67
  parent_node = get_node(parent_path, create: false)
71
68
  return false unless parent_node
72
69
 
70
+ node = if node_name == "" # path == "/"
71
+ parent_node
72
+ else
73
+ parent_node[node_name]
74
+ end
75
+ obj = node&.object
76
+ raise ArgumentError, "Cannot unexport, no object at #{path}" unless obj
77
+
73
78
  object_manager_for(obj)&.object_removed(obj)
74
- obj.connection = nil
75
- parent_node.delete(node_name).object
79
+ obj.object_server = nil
80
+ node.object = nil
81
+
82
+ # node can be deleted if
83
+ # - it has no children
84
+ # - it is not root
85
+ if node.empty? && !node.equal?(parent_node)
86
+ parent_node.delete(node_name)
87
+ end
88
+
89
+ obj
76
90
  end
77
91
 
78
92
  # Find the (closest) parent of *object*
@@ -98,6 +112,22 @@ module DBus
98
112
  node.descendant_objects
99
113
  end
100
114
 
115
+ # @param obj_or_path [DBus::Object,ObjectPath,String] an object or a valid object path
116
+ # @return [ObjectPath]
117
+ # @api private
118
+ def self.path_of(obj_or_path)
119
+ case obj_or_path
120
+ when ObjectPath
121
+ obj_or_path
122
+ when String
123
+ ObjectPath.new(obj_or_path)
124
+ when DBus::Object
125
+ obj_or_path.path
126
+ else
127
+ raise ArgumentError, "Expecting a DBus::Object argument or DBus::ObjectPath or String which parses as one"
128
+ end
129
+ end
130
+
101
131
  #########
102
132
 
103
133
  private
@@ -0,0 +1,97 @@
1
+ <!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
2
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
3
+ <node>
4
+ <interface name="org.freedesktop.DBus.Introspectable">
5
+ <method name="Introspect">
6
+ <arg direction="out" type="s"/>
7
+ </method>
8
+ </interface>
9
+ <interface name="org.freedesktop.DBus">
10
+ <method name="Hello">
11
+ <arg direction="out" type="s"/>
12
+ </method>
13
+ <method name="RequestName">
14
+ <arg direction="in" type="s"/>
15
+ <arg direction="in" type="u"/>
16
+ <arg direction="out" type="u"/>
17
+ </method>
18
+ <method name="ReleaseName">
19
+ <arg direction="in" type="s"/>
20
+ <arg direction="out" type="u"/>
21
+ </method>
22
+ <method name="StartServiceByName">
23
+ <arg direction="in" type="s"/>
24
+ <arg direction="in" type="u"/>
25
+ <arg direction="out" type="u"/>
26
+ </method>
27
+ <method name="UpdateActivationEnvironment">
28
+ <arg direction="in" type="a{ss}"/>
29
+ </method>
30
+ <method name="NameHasOwner">
31
+ <arg direction="in" type="s"/>
32
+ <arg direction="out" type="b"/>
33
+ </method>
34
+ <method name="ListNames">
35
+ <arg direction="out" type="as"/>
36
+ </method>
37
+ <method name="ListActivatableNames">
38
+ <arg direction="out" type="as"/>
39
+ </method>
40
+ <method name="AddMatch">
41
+ <arg direction="in" type="s"/>
42
+ </method>
43
+ <method name="RemoveMatch">
44
+ <arg direction="in" type="s"/>
45
+ </method>
46
+ <method name="GetNameOwner">
47
+ <arg direction="in" type="s"/>
48
+ <arg direction="out" type="s"/>
49
+ </method>
50
+ <method name="ListQueuedOwners">
51
+ <arg direction="in" type="s"/>
52
+ <arg direction="out" type="as"/>
53
+ </method>
54
+ <method name="GetConnectionUnixUser">
55
+ <arg direction="in" type="s"/>
56
+ <arg direction="out" type="u"/>
57
+ </method>
58
+ <method name="GetConnectionUnixProcessID">
59
+ <arg direction="in" type="s"/>
60
+ <arg direction="out" type="u"/>
61
+ </method>
62
+ <method name="GetAdtAuditSessionData">
63
+ <arg direction="in" type="s"/>
64
+ <arg direction="out" type="ay"/>
65
+ </method>
66
+ <method name="GetConnectionSELinuxSecurityContext">
67
+ <arg direction="in" type="s"/>
68
+ <arg direction="out" type="ay"/>
69
+ </method>
70
+ <method name="ReloadConfig">
71
+ </method>
72
+ <method name="GetId">
73
+ <arg direction="out" type="s"/>
74
+ </method>
75
+ <method name="GetConnectionCredentials">
76
+ <arg direction="in" type="s"/>
77
+ <arg direction="out" type="a{sv}"/>
78
+ </method>
79
+ <property name="Features" type="as" access="read">
80
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
81
+ </property>
82
+ <property name="Interfaces" type="as" access="read">
83
+ <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
84
+ </property>
85
+ <signal name="NameOwnerChanged">
86
+ <arg type="s"/>
87
+ <arg type="s"/>
88
+ <arg type="s"/>
89
+ </signal>
90
+ <signal name="NameLost">
91
+ <arg type="s"/>
92
+ </signal>
93
+ <signal name="NameAcquired">
94
+ <arg type="s"/>
95
+ </signal>
96
+ </interface>
97
+ </node>
@@ -22,11 +22,12 @@ module DBus
22
22
  # p manager.ListImages
23
23
  class ProxyService < NodeTree
24
24
  # @return [BusName,nil] The service name.
25
- # May be nil for peer connections
25
+ # Will be nil for a {PeerConnection}
26
26
  attr_reader :name
27
27
  # @return [Connection] The connection we're using.
28
28
  attr_reader :connection
29
29
 
30
+ # @param connection [Connection] The connection we're using.
30
31
  def initialize(name, connection)
31
32
  @name = BusName.new(name)
32
33
  @connection = connection
@@ -76,7 +77,7 @@ module DBus
76
77
  # Perform a recursive retrospection on the given current _node_
77
78
  # on the given _path_.
78
79
  def rec_introspect(node, path)
79
- xml = bus.introspect_data(@name, path)
80
+ xml = connection.introspect_data(@name, path)
80
81
  intfs, subnodes = IntrospectXMLParser.new(xml).parse
81
82
  subnodes.each do |nodename|
82
83
  subnode = node[nodename] = Node.new(nodename)
@@ -92,4 +93,15 @@ module DBus
92
93
  node.object = ProxyObjectFactory.new(xml, @connection, @name, path).build
93
94
  end
94
95
  end
96
+
97
+ # A hack for pretending that a {PeerConnection} has a single unnamed {ProxyService}
98
+ # so that we can get {ProxyObject}s from it.
99
+ class ProxyPeerService < ProxyService
100
+ # @param connection [Connection] The peer connection we're using.
101
+ def initialize(connection)
102
+ # this way we disallow ProxyService taking a nil name by accident
103
+ super(":0.0", connection)
104
+ @name = nil
105
+ end
106
+ end
95
107
  end
data/lib/dbus.rb CHANGED
@@ -26,6 +26,7 @@ require_relative "dbus/api_options"
26
26
  require_relative "dbus/auth"
27
27
  require_relative "dbus/bus"
28
28
  require_relative "dbus/bus_name"
29
+ require_relative "dbus/connection"
29
30
  require_relative "dbus/data"
30
31
  require_relative "dbus/emits_changed_signal"
31
32
  require_relative "dbus/error"