In Files

Parent

Files

DBus::Connection

D-Bus main connection class

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

Attributes

socket[R]

The socket that is used to connect with the bus.

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 198
def initialize(path)
  @path = path
  @unique_name = nil
  @buffer = ""
  @method_call_replies = Hash.new
  @method_call_msgs = Hash.new
  @signal_matchrules = Hash.new
  @proxy = nil
  @object_root = Node.new("/")
  @is_tcp = false
end

Public Instance Methods

[](name) click to toggle source
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 591
def add_match(mr, &slot)
  # check this is a signal.
  mrs = mr.to_s
  puts "#{@signal_matchrules.size} rules, adding #{mrs.inspect}" if $DEBUG
  # don't ask for the same match if we override it
  unless @signal_matchrules.key?(mrs)
    puts "Asked for a new match" if $DEBUG
    proxy.AddMatch(mrs)
  end
  @signal_matchrules[mrs] = slot
end
connect() click to toggle source

Connect to the bus and initialize the connection.

# File lib/dbus/bus.rb, line 211
def connect
  addresses = @path.split ";"
  # connect to first one that succeeds
  worked = addresses.find do |a|
    transport, keyvaluestring = a.split ":"
    kv_list = keyvaluestring.split ","
    kv_hash = Hash.new
    kv_list.each do |kv|
      key, escaped_value = kv.split "="
      value = escaped_value.gsub(/%(..)/) {|m| [$1].pack "H2" }
      kv_hash[key] = value
    end
    case transport
      when "unix"
      connect_to_unix kv_hash
      when "tcp"
      connect_to_tcp kv_hash
      else
      # ignore, report?
    end
  end
  worked
  # returns the address that worked or nil.
  # how to report failure?
end
connect_to_tcp(params) click to toggle source

Connect to a bus over tcp and initialize the connection.

# File lib/dbus/bus.rb, line 238
def connect_to_tcp(params)
  #check if the path is sufficient
  if params.key?("host") and params.key?("port")
    begin
      #initialize the tcp socket
      @socket = TCPSocket.new(params["host"],params["port"].to_i)
      @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
      init_connection
      @is_tcp = true
    rescue
      puts "Error: Could not establish connection to: #{@path}, will now exit."
      exit(0) #a little harsh
    end
  else
    #Danger, Will Robinson: the specified "path" is not usable
    puts "Error: supplied path: #{@path}, unusable! sorry."
  end
end
connect_to_unix(params) click to toggle source

Connect to an abstract unix bus and initialize the connection.

# File lib/dbus/bus.rb, line 258
def connect_to_unix(params)
  @socket = Socket.new(Socket::Constants::PF_UNIX,Socket::Constants::SOCK_STREAM, 0)
  @socket.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
  if ! params['abstract'].nil?
    if HOST_END == LIL_END
      sockaddr = "\11\\00\\00##{params['abstract']}"
    else
      sockaddr = "\00\\11\\00##{params['abstract']}"
    end
  elsif ! params['path'].nil?
    sockaddr = Socket.pack_sockaddr_un(params['path'])
  end
  @socket.connect(sockaddr)
  init_connection
end
emit(service, obj, intf, sig, *args) click to toggle source

Emit a signal event for the given service, object obj, interface intf and signal sig with arguments args.

# File lib/dbus/bus.rb, line 674
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
  send(m.marshall)
end
glibize() click to toggle source

Tell a bus to register itself on the glib main loop

# File lib/dbus/bus.rb, line 280
def glibize
  require 'glib2'
  # Circumvent a ruby-glib bug
  @channels ||= Array.new

  gio = GLib::IOChannel.new(@socket.fileno)
  @channels << gio
  gio.add_watch(GLib::IOChannel::IN) do |c, ch|
    update_buffer
    messages.each do |msg|
      process(msg)
    end
    true
  end
end
introspect(dest, path) click to toggle source

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 443
def introspect(dest, path)
  if not block_given?
    # introspect in synchronous !
    data = introspect_data(dest, path)
    pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
    return 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
# File lib/dbus/bus.rb, line 416
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
messages() click to toggle source

Retrieve all the messages that are currently in the buffer.

# File lib/dbus/bus.rb, line 520
def messages
  ret = Array.new
  while msg = pop_message
    ret << msg
  end
  ret
end
on_return(m, &retc) click to toggle source

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

# File lib/dbus/bus.rb, line 580
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
poll_messages() click to toggle source

Update the buffer and retrieve all messages using Connection#messages. Return the messages.

# File lib/dbus/bus.rb, line 533
def poll_messages
  ret = nil
  r, d, d = IO.select([@socket], nil, nil, 0)
  if r and r.size > 0
    update_buffer
  end
  messages
end
pop_message() click to toggle source

Get one message from the bus and remove it from the buffer. Return the message.

# File lib/dbus/bus.rb, line 507
def pop_message
  return nil if @buffer.empty?
  ret = nil
  begin
    ret, size = Message.new.unmarshall_buffer(@buffer)
    @buffer.slice!(0, size)
  rescue IncompleteBufferException => e
    # fall through, let ret be null
  end
  ret
end
process(m) click to toggle source

Process a message m based on its type.

# File lib/dbus/bus.rb, line 614
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 not mcs
      puts "DEBUG: no return code for mcs: #{mcs.inspect} m: #{m.inspect}" if $DEBUG
    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"
      puts "DEBUG: Got method call on /org/freedesktop/DBus" if $DEBUG
    end
    node = @service.get_node(m.path)
    if not node
      reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject",
                            "Object #{m.path} doesn't exist")
      send(reply.marshall)
    # handle introspectable as an exception:
    elsif m.interface == "org.freedesktop.DBus.Introspectable" and
        m.member == "Introspect"
      reply = Message.new(Message::METHOD_RETURN).reply_to(m)
      reply.sender = @unique_name
      reply.add_param(Type::STRING, node.to_xml)
      send(reply.marshall)
    else
      obj = node.object
      return if obj.nil?    # FIXME, sends no reply
      obj.dispatch(m) if obj
    end
  when DBus::Message::SIGNAL
    # the signal can match multiple different rules
    @signal_matchrules.each do |mrs, slot|
      if DBus::MatchRule.new.from_s(mrs).match(m)
        slot.call(m)
      end
    end
  else
    puts "DEBUG: Unknown message type: #{m.message_type}" if $DEBUG
  end
end
proxy() click to toggle source

Set up a ProxyObject for the bus itself, since the bus is introspectable. Returns the object.

# File lib/dbus/bus.rb, line 482
def proxy
  if @proxy == nil
    path = "/org/freedesktop/DBus"
    dest = "org.freedesktop.DBus"
    pof = DBus::ProxyObjectFactory.new(DBUSXMLINTRO, self, dest, path)
    @proxy = pof.build["org.freedesktop.DBus"]
  end
  @proxy
end
remove_match(mr) click to toggle source
# File lib/dbus/bus.rb, line 603
def remove_match(mr)
  mrs = mr.to_s
  unless @signal_matchrules.delete(mrs).nil?
    # don't remove nonexisting matches.
    # 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
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.

# File lib/dbus/bus.rb, line 464
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|
    if rmsg.is_a?(Error)  # check and report errors first
      raise rmsg
    elsif r != REQUEST_NAME_REPLY_PRIMARY_OWNER
      raise NameRequestError
    end
  end
  @service = Service.new(name, self)
  @service
end
send(buf) click to toggle source

Send the buffer buf to the bus using Connection#writel.

# File lib/dbus/bus.rb, line 275
def send(buf)
  @socket.write(buf) unless @socket.nil?
end
send_sync(m) click to toggle source

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 561
def send_sync(m, &retc) # :yields: reply/return message
  return if m.nil? #check if somethings wrong
  send(m.marshall)
  @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.has_key? m.serial
    retm = wait_for_message
    process(retm)
  end
end
send_sync_or_async(message, &reply_handler) click to toggle source

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 393
def send_sync_or_async(message, &reply_handler)
  ret = nil
  if reply_handler.nil?
    send_sync(message) do |rmsg|
      if rmsg.is_a?(Error)
        raise rmsg
      else
        ret = rmsg.params
      end
    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
    send(message.marshall)
  end
  ret
end
service(name) click to toggle source

Retrieves the Service with the given name.

# File lib/dbus/bus.rb, line 665
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: []
update_buffer() click to toggle source

Fill (append) the buffer from data that might be available on the socket.

# File lib/dbus/bus.rb, line 494
def update_buffer
  @buffer += @socket.read_nonblock(MSG_BUF_SIZE)  
rescue EOFError
  raise                     # the caller expects it
rescue Exception => e
  puts "Oops:", e
  raise if @is_tcp          # why?
  puts "WARNING: read_nonblock failed, falling back to .recv"
  @buffer += @socket.recv(MSG_BUF_SIZE)  
end
wait_for_message() click to toggle source

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

# File lib/dbus/bus.rb, line 543
def wait_for_message
  if @socket.nil?
    puts "ERROR: Can't wait for messages, @socket is nil."
    return
  end
  ret = pop_message
  while ret == nil
    r, d, d = IO.select([@socket])
    if r and r[0] == @socket
      update_buffer
      ret = pop_message
    end
  end
  ret
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.