# 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