# 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

DBUSXMLINTRO
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

unique_name[R]

The unique name (by specification) of the message.

### 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/bus.rb
    205 def initialize(path)
    206   @message_queue = MessageQueue.new(path)
    207   @unique_name = nil
    208   @method_call_replies = {}
    209   @method_call_msgs = {}
    210   @signal_matchrules = {}
    211   @proxy = nil
    212   @object_root = Node.new("/")
    213 end

### Public Instance Methods

[](name)
Alias for: service
add_match(mr, &slot) click to toggle source

Asks bus to send us messages matching mr, and execute slot when received

        # File lib/dbus/bus.rb
    480 def add_match(mr, &slot)
    481   # check this is a signal.
    482   mrs = mr.to_s
    483   DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}"
    484   # don't ask for the same match if we override it
    485   unless @signal_matchrules.key?(mrs)
    486     DBus.logger.debug "Asked for a new match"
    487     proxy.AddMatch(mrs)
    488   end
    489   @signal_matchrules[mrs] = slot
    490 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/bus.rb
    218 def dispatch_message_queue
    219   while (msg = @message_queue.pop(:non_block)) # FIXME: EOFError
    220     process(msg)
    221   end
    222 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.

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

glibize() click to toggle source

Tell a bus to register itself on the glib main loop

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

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/bus.rb
    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

introspect_data(dest, path, &reply_handler) click to toggle source

@api private

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

on_return(m, &retc) click to toggle source

@api private Specify a code block that has to be executed when a reply for message m is received.

        # File lib/dbus/bus.rb
    469 def on_return(m, &retc)
    470   # Have a better exception here
    471   if m.message_type != Message::METHOD_CALL
    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
    476 end

process(m) click to toggle source

@api private Process a message m based on its type.

        # File lib/dbus/bus.rb
    504 def process(m)
    505   return if m.nil? # check if somethings wrong
    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

proxy() click to toggle source

Set up a `ProxyObject` for the bus itself, since the bus is introspectable. @return [ProxyObject] that always returns an array

    ({DBus::ApiOptions#proxy_method_returns_array})

Returns the object.

        # File lib/dbus/bus.rb
    425 def proxy
    426   if @proxy.nil?
    427     path = "/org/freedesktop/DBus"
    428     dest = "org.freedesktop.DBus"
    429     pof = DBus::ProxyObjectFactory.new(
    430       DBUSXMLINTRO, self, dest, path,
    431       api: ApiOptions::A0
    432     )
    433     @proxy = pof.build["org.freedesktop.DBus"]
    434   end
    435   @proxy
    436 end

remove_match(mr) click to toggle source

        # File lib/dbus/bus.rb
    492 def remove_match(mr)
    493   mrs = mr.to_s
    494   rule_existed = @signal_matchrules.delete(mrs).nil?
    495   # don't remove nonexisting matches.
    496   return if rule_existed
    497   # FIXME: if we do try, the Error.MatchRuleNotFound is *not* raised
    498   # and instead is reported as "no return code for nil"
    499   proxy.RemoveMatch(mrs)
    500 end

request_service(name) click to toggle source

Attempt to request a service name.

FIXME, `NameRequestError` cannot really be rescued as it will be raised when dispatching a later call. Rework the API to better match the spec. @return [Service]

        # File lib/dbus/bus.rb
    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
    419 end

send_sync(m) { |reply| ... } click to toggle source

@api private Send a message m on to the bus. This is done synchronously, thus the call will block until a reply message arrives.

        # File lib/dbus/bus.rb
    447 def send_sync(m, &retc) # :yields: reply/return message
    448   return if m.nil? # check if somethings wrong
    449   @message_queue.push(m)
    450   @method_call_msgs[m.serial] = m
    451   @method_call_replies[m.serial] = retc
    452
    453   retm = wait_for_message
    454   return if retm.nil? # check if somethings wrong
    455
    456   process(retm)
    457   while @method_call_replies.key? m.serial
    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
    464 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/bus.rb
    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

service(name) click to toggle source

Retrieves the `Service` with the given name. @return [Service]

        # File lib/dbus/bus.rb
    559 def service(name)
    560   # The service might not exist at this time so we cannot really check
    561   # anything
    562   Service.new(name, self)
    563 end

Also aliased as: []
wait_for_message() click to toggle source

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

        # File lib/dbus/bus.rb
    440 def wait_for_message
    441   @message_queue.pop # FIXME: EOFError
    442 end

### Private Instance Methods

send_hello() click to toggle source

Send a hello messages to the bus to let it know we are here.

        # File lib/dbus/bus.rb
    587 def send_hello
    588   m = Message.new(DBus::Message::METHOD_CALL)
    589   m.path = "/org/freedesktop/DBus"
    590   m.destination = "org.freedesktop.DBus"
    591   m.interface = "org.freedesktop.DBus"
    592   m.member = "Hello"
    593   send_sync(m) do |rmsg|
    594     @unique_name = rmsg.destination
    595     DBus.logger.debug "Got hello reply. Our unique_name is #{@unique_name}"
    596   end
    597   @service = Service.new(@unique_name, self)
    598 end