Object
D-Bus main connection class
Main class that maintains a connection to a bus and can handle incoming and outgoing messages.
The buffer size for messages.
FIXME: describe the following names, flags and constants. See DBus spec for definition
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
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 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 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 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 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
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
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
# 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
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
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
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
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 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
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
# 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
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 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 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 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
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
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 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
Generated with the Darkfish Rdoc Generator 2.