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
- NAME_FLAG_ALLOW_REPLACEMENT
FIXME: describe the following names, flags and constants. See
DBusspec 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 @return [MessageQueue]
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/connection.rb 28 def initialize(path) 29 @message_queue = MessageQueue.new(path) 30 31 # @return [Hash{Integer => Proc}] 32 # key: message serial 33 # value: block to be run when the reply to that message is received 34 @method_call_replies = {} 35 36 # @return [Hash{Integer => Message}] 37 # for debugging only: messages for which a reply was not received yet; 38 # key == value.serial 39 @method_call_msgs = {} 40 @signal_matchrules = {} 41 end
Public Instance Methods
Asks bus to send us messages matching mr, and execute slot when received @param match_rule [MatchRule,#to_s] @return [void] actually return whether the rule existed, internal detail
# File lib/dbus/connection.rb 253 def add_match(match_rule, &slot) 254 # check this is a signal. 255 mrs = match_rule.to_s 256 DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}" 257 rule_existed = @signal_matchrules.key?(mrs) 258 @signal_matchrules[mrs] = slot 259 rule_existed 260 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/connection.rb 50 def dispatch_message_queue 51 while (msg = @message_queue.pop(blocking: false)) # FIXME: EOFError 52 process(msg) 53 end 54 end
@api private Emit a signal event for the given service, object obj, interface intf and signal sig with arguments args. @param _service unused @param obj [DBus::Object] @param intf [Interface] @param sig [Signal] @param args arguments for the signal
# File lib/dbus/connection.rb 337 def emit(_service, obj, intf, sig, *args) 338 m = Message.new(DBus::Message::SIGNAL) 339 m.path = obj.path 340 m.interface = intf.name 341 m.member = sig.name 342 i = 0 343 sig.params.each do |par| 344 m.add_param(par.type, args[i]) 345 i += 1 346 end 347 @message_queue.push(m) 348 end
Tell a bus to register itself on the glib main loop
# File lib/dbus/connection.rb 57 def glibize 58 require "glib2" 59 # Circumvent a ruby-glib bug 60 @channels ||= [] 61 62 gio = GLib::IOChannel.new(@message_queue.socket.fileno) 63 @channels << gio 64 gio.add_watch(GLib::IOChannel::IN) do |_c, _ch| 65 dispatch_message_queue 66 true 67 end 68 end
In case RequestName did not succeed, raise an exception but first ask the bus who owns the name instead of us @param ret [Integer] what RequestName returned @param name Name that was requested @return [REQUEST_NAME_REPLY_PRIMARY_OWNER,REQUEST_NAME_REPLY_ALREADY_OWNER] on success @raise [NameRequestError] with error_code REQUEST_NAME_REPLY_EXISTS or REQUEST_NAME_REPLY_IN_QUEUE, on failure @api private
# File lib/dbus/connection.rb 173 def handle_return_of_request_name(ret, name) 174 if [REQUEST_NAME_REPLY_EXISTS, REQUEST_NAME_REPLY_IN_QUEUE].include?(ret) 175 other = proxy.GetNameOwner(name).first 176 other_creds = proxy.GetConnectionCredentials(other).first 177 message = "Could not request #{name}, already owned by #{other}, #{other_creds.inspect}" 178 raise NameRequestError.new(ret), message 179 end 180 181 ret 182 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/connection.rb 141 def introspect(dest, path) 142 if !block_given? 143 # introspect in synchronous ! 144 data = introspect_data(dest, path) 145 pof = DBus::ProxyObjectFactory.new(data, self, dest, path) 146 pof.build 147 else 148 introspect_data(dest, path) do |async_data| 149 yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build) 150 end 151 end 152 end
@api private
# File lib/dbus/connection.rb 113 def introspect_data(dest, path, &reply_handler) 114 m = DBus::Message.new(DBus::Message::METHOD_CALL) 115 m.path = path 116 m.interface = "org.freedesktop.DBus.Introspectable" 117 m.destination = dest 118 m.member = "Introspect" 119 m.sender = unique_name 120 if reply_handler.nil? 121 send_sync_or_async(m).first 122 else 123 send_sync_or_async(m) do |*args| 124 # TODO: test async introspection, is it used at all? 125 args.shift # forget the message, pass only the text 126 reply_handler.call(*args) 127 nil 128 end 129 end 130 end
# File lib/dbus/connection.rb 43 def object_server 44 @object_server ||= ObjectServer.new(self) 45 end
@api private Specify a code block that has to be executed when a reply for message msg is received. @param msg [Message]
# File lib/dbus/connection.rb 239 def on_return(msg, &retc) 240 # Have a better exception here 241 if msg.message_type != Message::METHOD_CALL 242 raise "on_return should only get method_calls" 243 end 244 245 @method_call_msgs[msg.serial] = msg 246 @method_call_replies[msg.serial] = retc 247 end
@api private Process a message msg based on its type. @param msg [Message]
# File lib/dbus/connection.rb 272 def process(msg) 273 return if msg.nil? # check if somethings wrong 274 275 case msg.message_type 276 when Message::ERROR, Message::METHOD_RETURN 277 raise InvalidPacketException if msg.reply_serial.nil? 278 279 mcs = @method_call_replies[msg.reply_serial] 280 if !mcs 281 DBus.logger.debug "no return code for mcs: #{mcs.inspect} msg: #{msg.inspect}" 282 else 283 if msg.message_type == Message::ERROR 284 mcs.call(Error.new(msg)) 285 else 286 mcs.call(msg) 287 end 288 @method_call_replies.delete(msg.reply_serial) 289 @method_call_msgs.delete(msg.reply_serial) 290 end 291 when DBus::Message::METHOD_CALL 292 if msg.path == "/org/freedesktop/DBus" 293 DBus.logger.debug "Got method call on /org/freedesktop/DBus" 294 end 295 node = object_server.get_node(msg.path, create: false) 296 # introspect a known path even if there is no object on it 297 if node && 298 msg.interface == "org.freedesktop.DBus.Introspectable" && 299 msg.member == "Introspect" 300 reply = Message.new(Message::METHOD_RETURN).reply_to(msg) 301 reply.sender = @unique_name 302 xml = node.to_xml(msg.path) 303 reply.add_param(Type::STRING, xml) 304 @message_queue.push(reply) 305 # dispatch for an object 306 elsif node&.object 307 node.object.dispatch(msg) 308 else 309 reply = Message.error(msg, "org.freedesktop.DBus.Error.UnknownObject", 310 "Object #{msg.path} doesn't exist") 311 @message_queue.push(reply) 312 end 313 when DBus::Message::SIGNAL 314 # the signal can match multiple different rules 315 # clone to allow new signale handlers to be registered 316 @signal_matchrules.dup.each do |mrs, slot| 317 if DBus::MatchRule.new.from_s(mrs).match(msg) 318 slot.call(msg) 319 end 320 end 321 else 322 # spec(Message Format): Unknown types must be ignored. 323 DBus.logger.debug "Unknown message type: #{msg.message_type}" 324 end 325 rescue Exception => e 326 raise msg.annotate_exception(e) 327 end
@param match_rule [MatchRule,#to_s] @return [void] actually return whether the rule existed, internal detail
# File lib/dbus/connection.rb 264 def remove_match(match_rule) 265 mrs = match_rule.to_s 266 @signal_matchrules.delete(mrs).nil? 267 end
Attempt to request a service name. @raise NameRequestError which cannot really be rescued as it will be raised when dispatching a later call. @return [ObjectServer] @deprecated Use {BusConnection#request_name}.
# File lib/dbus/connection.rb 188 def request_service(name) 189 # Use RequestName, but asynchronously! 190 # A synchronous call would not work with service activation, where 191 # method calls to be serviced arrive before the reply for RequestName 192 # (Ticket#29). 193 proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r| 194 # check and report errors first 195 raise rmsg if rmsg.is_a?(Error) 196 197 handle_return_of_request_name(r, name) 198 end 199 object_server 200 end
@api private Send a message msg on to the bus. This is done synchronously, thus the call will block until a reply message arrives. @param msg [Message] @param retc [Proc] the reply handler @yieldparam rmsg [MethodReturnMessage] the reply @yieldreturn [Array<Object>] the reply (out) parameters
# File lib/dbus/connection.rb 215 def send_sync(msg, &retc) # :yields: reply/return message 216 return if msg.nil? # check if somethings wrong 217 218 @message_queue.push(msg) 219 @method_call_msgs[msg.serial] = msg 220 @method_call_replies[msg.serial] = retc 221 222 retm = wait_for_message 223 return if retm.nil? # check if somethings wrong 224 225 process(retm) 226 while @method_call_replies.key? msg.serial 227 retm = wait_for_message 228 process(retm) 229 end 230 rescue EOFError 231 new_err = DBus::Error.new("Connection dropped after we sent #{msg.inspect}") 232 raise new_err 233 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/connection.rb 91 def send_sync_or_async(message, &reply_handler) 92 ret = nil 93 if reply_handler.nil? 94 send_sync(message) do |rmsg| 95 raise rmsg if rmsg.is_a?(Error) 96 97 ret = rmsg.params 98 end 99 else 100 on_return(message) do |rmsg| 101 if rmsg.is_a?(Error) 102 reply_handler.call(rmsg) 103 else 104 reply_handler.call(rmsg, * rmsg.params) 105 end 106 end 107 @message_queue.push(message) 108 end 109 ret 110 end
@api private Wait for a message to arrive. Return it once it is available.
# File lib/dbus/connection.rb 204 def wait_for_message 205 @message_queue.pop # FIXME: EOFError 206 end