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
pop and push messages here
The unique name (by specification) of the message.
Public Class Methods
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) @message_queue = MessageQueue.new(path) @unique_name = nil @method_call_replies = Hash.new @method_call_msgs = Hash.new @signal_matchrules = Hash.new @proxy = nil @object_root = Node.new("/") end
Public Instance Methods
Asks bus to send us messages matching mr, and execute slot when received
# File lib/dbus/bus.rb, line 470 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 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 211 def dispatch_message_queue while (msg = @message_queue.pop(:non_block)) # FIXME EOFError process(msg) end end
@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 558 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
Tell a bus to register itself on the glib main loop
# File lib/dbus/bus.rb, line 218 def glibize require 'glib2' # Circumvent a ruby-glib bug @channels ||= Array.new 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
@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 381 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
@api private
# File lib/dbus/bus.rb, line 353 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
@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 459 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
@api private Process a message m based on its type.
# File lib/dbus/bus.rb, line 494 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 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 not 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" 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) @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 @signal_matchrules.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
Set up a ProxyObject
for the bus itself, since the bus is introspectable. Returns the object.
# File lib/dbus/bus.rb, line 421 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
# File lib/dbus/bus.rb, line 482 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
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 403 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
@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 440 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.has_key? m.serial retm = wait_for_message process(retm) end end
@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 329 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 @message_queue.push(message) end ret end
Retrieves the Service
with the given name. @return [Service]
# File lib/dbus/bus.rb, line 548 def service(name) # The service might not exist at this time so we cannot really check # anything Service.new(name, self) end
@api private Wait for a message to arrive. Return it once it is available.
# File lib/dbus/bus.rb, line 433 def wait_for_message @message_queue.pop # FIXME EOFError end
Private Instance Methods
Send a hello messages to the bus to let it know we are here.
# File lib/dbus/bus.rb, line 576 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