class DBus::Connection

D-Bus main connection class

Main class that maintains a connection to a bus and can handle incoming and outgoing messages.

Constants

NAME_FLAG_ALLOW_REPLACEMENT

FIXME: describe the following names, flags and constants. See DBus spec for definition

NAME_FLAG_DO_NOT_QUEUE
NAME_FLAG_REPLACE_EXISTING
REQUEST_NAME_REPLY_ALREADY_OWNER
REQUEST_NAME_REPLY_EXISTS
REQUEST_NAME_REPLY_IN_QUEUE
REQUEST_NAME_REPLY_PRIMARY_OWNER

Attributes

message_queue[R]

pop and push messages here @return [MessageQueue]

Public Class Methods

new(path) click to toggle source

Create a new connection to the bus for a given connect path. path format is described in the D-Bus specification: dbus.freedesktop.org/doc/dbus-specification.html#addresses and is something like: “transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2” e.g. “unix:path=/tmp/dbus-test” or “tcp:host=localhost,port=2687”

   # File lib/dbus/connection.rb
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

Public Instance Methods

add_match(match_rule, &slot) click to toggle source

Asks bus to send us messages matching mr, and execute slot when received @param match_rule [MatchRule,#to_s] @return [void] actually return whether the rule existed, internal detail

    # File lib/dbus/connection.rb
253 def add_match(match_rule, &slot)
254   # check this is a signal.
255   mrs = match_rule.to_s
256   DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}"
257   rule_existed = @signal_matchrules.key?(mrs)
258   @signal_matchrules[mrs] = slot
259   rule_existed
260 end
dispatch_message_queue() click to toggle source

Dispatch all messages that are available in the queue, but do not block on the queue. Called by a main loop when something is available in the queue

   # File lib/dbus/connection.rb
50 def dispatch_message_queue
51   while (msg = @message_queue.pop(blocking: false)) # FIXME: EOFError
52     process(msg)
53   end
54 end
emit(_service, obj, intf, sig, *args) click to toggle source

@api private Emit a signal event for the given service, object obj, interface intf and signal sig with arguments args. @param _service unused @param obj [DBus::Object] @param intf [Interface] @param sig [Signal] @param args arguments for the signal

    # File lib/dbus/connection.rb
337 def emit(_service, obj, intf, sig, *args)
338   m = Message.new(DBus::Message::SIGNAL)
339   m.path = obj.path
340   m.interface = intf.name
341   m.member = sig.name
342   i = 0
343   sig.params.each do |par|
344     m.add_param(par.type, args[i])
345     i += 1
346   end
347   @message_queue.push(m)
348 end
glibize() click to toggle source

Tell a bus to register itself on the glib main loop

   # File lib/dbus/connection.rb
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
handle_return_of_request_name(ret, name) click to toggle source

In case RequestName did not succeed, raise an exception but first ask the bus who owns the name instead of us @param ret [Integer] what RequestName returned @param name Name that was requested @return [REQUEST_NAME_REPLY_PRIMARY_OWNER,REQUEST_NAME_REPLY_ALREADY_OWNER] on success @raise [NameRequestError] with error_code REQUEST_NAME_REPLY_EXISTS or REQUEST_NAME_REPLY_IN_QUEUE, on failure @api private

    # File lib/dbus/connection.rb
173 def handle_return_of_request_name(ret, name)
174   if [REQUEST_NAME_REPLY_EXISTS, REQUEST_NAME_REPLY_IN_QUEUE].include?(ret)
175     other = proxy.GetNameOwner(name).first
176     other_creds = proxy.GetConnectionCredentials(other).first
177     message = "Could not request #{name}, already owned by #{other}, #{other_creds.inspect}"
178     raise NameRequestError.new(ret), message
179   end
180 
181   ret
182 end
introspect(dest, path) { |proxy_object_factory.build| ... } click to toggle source

@api private Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method dest is the service and path the object path you want to introspect If a code block is given, the introspect call in asynchronous. If not data is returned

FIXME: link to ProxyObject data definition The returned object is a ProxyObject that has methods you can call to issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN

    # File lib/dbus/connection.rb
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
introspect_data(dest, path, &reply_handler) click to toggle source

@api private

    # File lib/dbus/connection.rb
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
object_server() click to toggle source
   # File lib/dbus/connection.rb
43 def object_server
44   @object_server ||= ObjectServer.new(self)
45 end
on_return(msg, &retc) click to toggle source

@api private Specify a code block that has to be executed when a reply for message msg is received. @param msg [Message]

    # File lib/dbus/connection.rb
239 def on_return(msg, &retc)
240   # Have a better exception here
241   if msg.message_type != Message::METHOD_CALL
242     raise "on_return should only get method_calls"
243   end
244 
245   @method_call_msgs[msg.serial] = msg
246   @method_call_replies[msg.serial] = retc
247 end
process(msg) click to toggle source

@api private Process a message msg based on its type. @param msg [Message]

    # File lib/dbus/connection.rb
272 def process(msg)
273   return if msg.nil? # check if somethings wrong
274 
275   case msg.message_type
276   when Message::ERROR, Message::METHOD_RETURN
277     raise InvalidPacketException if msg.reply_serial.nil?
278 
279     mcs = @method_call_replies[msg.reply_serial]
280     if !mcs
281       DBus.logger.debug "no return code for mcs: #{mcs.inspect} msg: #{msg.inspect}"
282     else
283       if msg.message_type == Message::ERROR
284         mcs.call(Error.new(msg))
285       else
286         mcs.call(msg)
287       end
288       @method_call_replies.delete(msg.reply_serial)
289       @method_call_msgs.delete(msg.reply_serial)
290     end
291   when DBus::Message::METHOD_CALL
292     if msg.path == "/org/freedesktop/DBus"
293       DBus.logger.debug "Got method call on /org/freedesktop/DBus"
294     end
295     node = object_server.get_node(msg.path, create: false)
296     # introspect a known path even if there is no object on it
297     if node &&
298        msg.interface == "org.freedesktop.DBus.Introspectable" &&
299        msg.member == "Introspect"
300       reply = Message.new(Message::METHOD_RETURN).reply_to(msg)
301       reply.sender = @unique_name
302       xml = node.to_xml(msg.path)
303       reply.add_param(Type::STRING, xml)
304       @message_queue.push(reply)
305     # dispatch for an object
306     elsif node&.object
307       node.object.dispatch(msg)
308     else
309       reply = Message.error(msg, "org.freedesktop.DBus.Error.UnknownObject",
310                             "Object #{msg.path} doesn't exist")
311       @message_queue.push(reply)
312     end
313   when DBus::Message::SIGNAL
314     # the signal can match multiple different rules
315     # clone to allow new signale handlers to be registered
316     @signal_matchrules.dup.each do |mrs, slot|
317       if DBus::MatchRule.new.from_s(mrs).match(msg)
318         slot.call(msg)
319       end
320     end
321   else
322     # spec(Message Format): Unknown types must be ignored.
323     DBus.logger.debug "Unknown message type: #{msg.message_type}"
324   end
325 rescue Exception => e
326   raise msg.annotate_exception(e)
327 end
remove_match(match_rule) click to toggle source

@param match_rule [MatchRule,#to_s] @return [void] actually return whether the rule existed, internal detail

    # File lib/dbus/connection.rb
264 def remove_match(match_rule)
265   mrs = match_rule.to_s
266   @signal_matchrules.delete(mrs).nil?
267 end
request_service(name) click to toggle source

Attempt to request a service name. @raise NameRequestError which cannot really be rescued as it will be raised when dispatching a later call. @return [ObjectServer] @deprecated Use {BusConnection#request_name}.

    # File lib/dbus/connection.rb
188 def request_service(name)
189   # Use RequestName, but asynchronously!
190   # A synchronous call would not work with service activation, where
191   # method calls to be serviced arrive before the reply for RequestName
192   # (Ticket#29).
193   proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r|
194     # check and report errors first
195     raise rmsg if rmsg.is_a?(Error)
196 
197     handle_return_of_request_name(r, name)
198   end
199   object_server
200 end
send_sync(msg) { |reply| ... } click to toggle source

@api private Send a message msg on to the bus. This is done synchronously, thus the call will block until a reply message arrives. @param msg [Message] @param retc [Proc] the reply handler @yieldparam rmsg [MethodReturnMessage] the reply @yieldreturn [Array<Object>] the reply (out) parameters

    # File lib/dbus/connection.rb
215 def send_sync(msg, &retc) # :yields: reply/return message
216   return if msg.nil? # check if somethings wrong
217 
218   @message_queue.push(msg)
219   @method_call_msgs[msg.serial] = msg
220   @method_call_replies[msg.serial] = retc
221 
222   retm = wait_for_message
223   return if retm.nil? # check if somethings wrong
224 
225   process(retm)
226   while @method_call_replies.key? msg.serial
227     retm = wait_for_message
228     process(retm)
229   end
230 rescue EOFError
231   new_err = DBus::Error.new("Connection dropped after we sent #{msg.inspect}")
232   raise new_err
233 end
send_sync_or_async(message, &reply_handler) click to toggle source

@api private Send a message. If reply_handler is not given, wait for the reply and return the reply, or raise the error. If reply_handler is given, it will be called when the reply eventually arrives, with the reply message as the 1st param and its params following

    # File lib/dbus/connection.rb
 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
wait_for_message() click to toggle source

@api private Wait for a message to arrive. Return it once it is available.

    # File lib/dbus/connection.rb
204 def wait_for_message
205   @message_queue.pop # FIXME: EOFError
206 end