# 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, line 205
    def initialize(path)
      @message_queue = MessageQueue.new(path)
      @unique_name = nil
      @method_call_replies = {}
      @method_call_msgs = {}
      @signal_matchrules = {}
      @proxy = nil
      @object_root = Node.new("/")
    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, line 477
    def add_match(mr, &slot)
      # check this is a signal.
      mrs = mr.to_s
      DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}"
      # don't ask for the same match if we override it
      unless @signal_matchrules.key?(mrs)
        DBus.logger.debug "Asked for a new match"
        proxy.AddMatch(mrs)
      end
      @signal_matchrules[mrs] = slot
    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, line 218
    def dispatch_message_queue
      while (msg = @message_queue.pop(:non_block)) # FIXME: EOFError
        process(msg)
      end
    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, line 566
    def emit(service, obj, intf, sig, *args)
      m = Message.new(DBus::Message::SIGNAL)
      m.path = obj.path
      m.interface = intf.name
      m.member = sig.name
      m.sender = service.name
      i = 0
      sig.params.each do |par|
        m.add_param(par.type, args[i])
        i += 1
      end
      @message_queue.push(m)
    end

glibize() click to toggle source

Tell a bus to register itself on the glib main loop

    # File lib/dbus/bus.rb, line 225
    def glibize
      require "glib2"
      # Circumvent a ruby-glib bug
      @channels ||= []

      gio = GLib::IOChannel.new(@message_queue.socket.fileno)
      @channels << gio
      gio.add_watch(GLib::IOChannel::IN) do |_c, _ch|
        dispatch_message_queue
        true
      end
    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, line 385
    def introspect(dest, path)
      if !block_given?
        # introspect in synchronous !
        data = introspect_data(dest, path)
        pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
        pof.build
      else
        introspect_data(dest, path) do |async_data|
          yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build)
        end
      end
    end

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

@api private

    # File lib/dbus/bus.rb, line 357
    def introspect_data(dest, path, &reply_handler)
      m = DBus::Message.new(DBus::Message::METHOD_CALL)
      m.path = path
      m.interface = "org.freedesktop.DBus.Introspectable"
      m.destination = dest
      m.member = "Introspect"
      m.sender = unique_name
      if reply_handler.nil?
        send_sync_or_async(m).first
      else
        send_sync_or_async(m) do |*args|
          # TODO: test async introspection, is it used at all?
          args.shift # forget the message, pass only the text
          reply_handler.call(*args)
          nil
        end
      end
    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, line 466
    def on_return(m, &retc)
      # Have a better exception here
      if m.message_type != Message::METHOD_CALL
        raise "on_return should only get method_calls"
      end
      @method_call_msgs[m.serial] = m
      @method_call_replies[m.serial] = retc
    end

process(m) click to toggle source

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

    # File lib/dbus/bus.rb, line 501
    def process(m)
      return if m.nil? # check if somethings wrong
      case m.message_type
      when Message::ERROR, Message::METHOD_RETURN
        raise InvalidPacketException if m.reply_serial.nil?
        mcs = @method_call_replies[m.reply_serial]
        if !mcs
          DBus.logger.debug "no return code for mcs: #{mcs.inspect} m: #{m.inspect}"
        else
          if m.message_type == Message::ERROR
            mcs.call(Error.new(m))
          else
            mcs.call(m)
          end
          @method_call_replies.delete(m.reply_serial)
          @method_call_msgs.delete(m.reply_serial)
        end
      when DBus::Message::METHOD_CALL
        if m.path == "/org/freedesktop/DBus"
          DBus.logger.debug "Got method call on /org/freedesktop/DBus"
        end
        node = @service.get_node(m.path)
        if !node
          reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject",
                                "Object #{m.path} doesn't exist")
          @message_queue.push(reply)
        # handle introspectable as an exception:
        elsif m.interface == "org.freedesktop.DBus.Introspectable" &&
              m.member == "Introspect"
          reply = Message.new(Message::METHOD_RETURN).reply_to(m)
          reply.sender = @unique_name
          reply.add_param(Type::STRING, node.to_xml)
          @message_queue.push(reply)
        else
          obj = node.object
          return if obj.nil? # FIXME, pushes no reply
          obj.dispatch(m) if obj
        end
      when DBus::Message::SIGNAL
        # the signal can match multiple different rules
        # clone to allow new signale handlers to be registered
        @signal_matchrules.dup.each do |mrs, slot|
          if DBus::MatchRule.new.from_s(mrs).match(m)
            slot.call(m)
          end
        end
      else
        DBus.logger.debug "Unknown message type: #{m.message_type}"
      end
    rescue Exception => ex
      raise m.annotate_exception(ex)
    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, line 425
    def proxy
      if @proxy.nil?
        path = "/org/freedesktop/DBus"
        dest = "org.freedesktop.DBus"
        pof = DBus::ProxyObjectFactory.new(
          DBUSXMLINTRO, self, dest, path,
          api: ApiOptions::A0
        )
        @proxy = pof.build["org.freedesktop.DBus"]
      end
      @proxy
    end

remove_match(mr) click to toggle source

    # File lib/dbus/bus.rb, line 489
    def remove_match(mr)
      mrs = mr.to_s
      rule_existed = @signal_matchrules.delete(mrs).nil?
      # don't remove nonexisting matches.
      return if rule_existed
      # FIXME: if we do try, the Error.MatchRuleNotFound is *not* raised
      # and instead is reported as "no return code for nil"
      proxy.RemoveMatch(mrs)
    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, line 407
    def request_service(name)
      # Use RequestName, but asynchronously!
      # A synchronous call would not work with service activation, where
      # method calls to be serviced arrive before the reply for RequestName
      # (Ticket#29).
      proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r|
        # check and report errors first
        raise rmsg if rmsg.is_a?(Error)
        raise NameRequestError unless r == REQUEST_NAME_REPLY_PRIMARY_OWNER
      end
      @service = Service.new(name, self)
      @service
    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, line 447
    def send_sync(m, &retc) # :yields: reply/return message
      return if m.nil? # check if somethings wrong
      @message_queue.push(m)
      @method_call_msgs[m.serial] = m
      @method_call_replies[m.serial] = retc

      retm = wait_for_message
      return if retm.nil? # check if somethings wrong

      process(retm)
      while @method_call_replies.key? m.serial
        retm = wait_for_message
        process(retm)
      end
    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, line 336
    def send_sync_or_async(message, &reply_handler)
      ret = nil
      if reply_handler.nil?
        send_sync(message) do |rmsg|
          raise rmsg if rmsg.is_a?(Error)
          ret = rmsg.params
        end
      else
        on_return(message) do |rmsg|
          if rmsg.is_a?(Error)
            reply_handler.call(rmsg)
          else
            reply_handler.call(rmsg, * rmsg.params)
          end
        end
        @message_queue.push(message)
      end
      ret
    end

service(name) click to toggle source

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

    # File lib/dbus/bus.rb, line 556
    def service(name)
      # The service might not exist at this time so we cannot really check
      # anything
      Service.new(name, self)
    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, line 440
    def wait_for_message
      @message_queue.pop # FIXME: EOFError
    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, line 584
    def send_hello
      m = Message.new(DBus::Message::METHOD_CALL)
      m.path = "/org/freedesktop/DBus"
      m.destination = "org.freedesktop.DBus"
      m.interface = "org.freedesktop.DBus"
      m.member = "Hello"
      send_sync(m) do |rmsg|
        @unique_name = rmsg.destination
        DBus.logger.debug "Got hello reply. Our unique_name is #{@unique_name}"
      end
      @service = Service.new(@unique_name, self)
    end