Class: DBus::Connection
- Inherits:
-
Object
- Object
- DBus::Connection
- Defined in:
- lib/dbus/connection.rb
Overview
D-Bus main connection class
Main class that maintains a connection to a bus and can handle incoming and outgoing messages.
Direct Known Subclasses
Defined Under Namespace
Classes: NameRequestError
Constant Summary collapse
- NAME_FLAG_ALLOW_REPLACEMENT =
FIXME: describe the following names, flags and constants. See DBus spec for definition
0x1
- NAME_FLAG_REPLACE_EXISTING =
0x2
- NAME_FLAG_DO_NOT_QUEUE =
0x4
- REQUEST_NAME_REPLY_PRIMARY_OWNER =
0x1
- REQUEST_NAME_REPLY_IN_QUEUE =
0x2
- REQUEST_NAME_REPLY_EXISTS =
0x3
- REQUEST_NAME_REPLY_ALREADY_OWNER =
0x4
Instance Attribute Summary collapse
-
#message_queue ⇒ MessageQueue
readonly
pop and push messages here.
Instance Method Summary collapse
-
#add_match(match_rule, &slot) ⇒ void
Asks bus to send us messages matching mr, and execute slot when received.
-
#dispatch_message_queue ⇒ Object
Dispatch all messages that are available in the queue, but do not block on the queue.
-
#emit(_service, obj, intf, sig, *args) ⇒ Object
private
Emit a signal event for the given service, object obj, interface intf and signal sig with arguments args.
-
#glibize ⇒ Object
Tell a bus to register itself on the glib main loop.
-
#handle_return_of_request_name(ret, name) ⇒ REQUEST_NAME_REPLY_PRIMARY_OWNER, REQUEST_NAME_REPLY_ALREADY_OWNER
private
In case RequestName did not succeed, raise an exception but first ask the bus who owns the name instead of us.
-
#initialize(path) ⇒ Connection
constructor
Create a new connection to the bus for a given connect path.
-
#introspect(dest, path) ⇒ Object
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.
- #introspect_data(dest, path, &reply_handler) ⇒ Object private
- #object_server ⇒ Object
-
#on_return(msg, &retc) ⇒ Object
private
Specify a code block that has to be executed when a reply for message msg is received.
-
#process(msg) ⇒ Object
private
Process a message msg based on its type.
-
#remove_match(match_rule) ⇒ void
Actually return whether the rule existed, internal detail.
- #request_service(name) ⇒ ObjectServer deprecated Deprecated.
-
#send_sync(msg, &retc) {|rmsg| ... } ⇒ Object
private
Send a message msg on to the bus.
-
#send_sync_or_async(message, &reply_handler) ⇒ Object
private
Send a message.
-
#wait_for_message ⇒ Object
private
Wait for a message to arrive.
Constructor Details
#initialize(path) ⇒ Connection
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”
28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
# File 'lib/dbus/connection.rb', line 28 def initialize(path) @message_queue = MessageQueue.new(path) # @return [Hash{Integer => Proc}] # key: message serial # value: block to be run when the reply to that message is received @method_call_replies = {} # @return [Hash{Integer => Message}] # for debugging only: messages for which a reply was not received yet; # key == value.serial @method_call_msgs = {} @signal_matchrules = {} end |
Instance Attribute Details
#message_queue ⇒ MessageQueue (readonly)
pop and push messages here
20 21 22 |
# File 'lib/dbus/connection.rb', line 20 def @message_queue end |
Instance Method Details
#add_match(match_rule, &slot) ⇒ void
This method returns an undefined value.
Asks bus to send us messages matching mr, and execute slot when received
253 254 255 256 257 258 259 260 |
# File 'lib/dbus/connection.rb', line 253 def add_match(match_rule, &slot) # check this is a signal. mrs = match_rule.to_s DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}" rule_existed = @signal_matchrules.key?(mrs) @signal_matchrules[mrs] = slot rule_existed end |
#dispatch_message_queue ⇒ Object
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
50 51 52 53 54 |
# File 'lib/dbus/connection.rb', line 50 def while (msg = @message_queue.pop(blocking: false)) # FIXME: EOFError process(msg) end end |
#emit(_service, obj, intf, sig, *args) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Emit a signal event for the given service, object obj, interface intf and signal sig with arguments args.
337 338 339 340 341 342 343 344 345 346 347 348 |
# File 'lib/dbus/connection.rb', line 337 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 i = 0 sig.params.each do |par| m.add_param(par.type, args[i]) i += 1 end @message_queue.push(m) end |
#glibize ⇒ Object
Tell a bus to register itself on the glib main loop
57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'lib/dbus/connection.rb', line 57 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| true end end |
#handle_return_of_request_name(ret, name) ⇒ REQUEST_NAME_REPLY_PRIMARY_OWNER, REQUEST_NAME_REPLY_ALREADY_OWNER
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
In case RequestName did not succeed, raise an exception but first ask the bus who owns the name instead of us
173 174 175 176 177 178 179 180 181 182 |
# File 'lib/dbus/connection.rb', line 173 def handle_return_of_request_name(ret, name) if [REQUEST_NAME_REPLY_EXISTS, REQUEST_NAME_REPLY_IN_QUEUE].include?(ret) other = proxy.GetNameOwner(name).first other_creds = proxy.GetConnectionCredentials(other).first = "Could not request #{name}, already owned by #{other}, #{other_creds.inspect}" raise NameRequestError.new(ret), end ret end |
#introspect(dest, path) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
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
141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/dbus/connection.rb', line 141 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) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 |
# File 'lib/dbus/connection.rb', line 113 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 |
#object_server ⇒ Object
43 44 45 |
# File 'lib/dbus/connection.rb', line 43 def object_server @object_server ||= ObjectServer.new(self) end |
#on_return(msg, &retc) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Specify a code block that has to be executed when a reply for message msg is received.
239 240 241 242 243 244 245 246 247 |
# File 'lib/dbus/connection.rb', line 239 def on_return(msg, &retc) # Have a better exception here if msg. != Message::METHOD_CALL raise "on_return should only get method_calls" end @method_call_msgs[msg.serial] = msg @method_call_replies[msg.serial] = retc end |
#process(msg) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Process a message msg based on its type.
272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 |
# File 'lib/dbus/connection.rb', line 272 def process(msg) return if msg.nil? # check if somethings wrong case msg. when Message::ERROR, Message::METHOD_RETURN raise InvalidPacketException if msg.reply_serial.nil? mcs = @method_call_replies[msg.reply_serial] if !mcs DBus.logger.debug "no return code for mcs: #{mcs.inspect} msg: #{msg.inspect}" else if msg. == Message::ERROR mcs.call(Error.new(msg)) else mcs.call(msg) end @method_call_replies.delete(msg.reply_serial) @method_call_msgs.delete(msg.reply_serial) end when DBus::Message::METHOD_CALL if msg.path == "/org/freedesktop/DBus" DBus.logger.debug "Got method call on /org/freedesktop/DBus" end node = object_server.get_node(msg.path, create: false) # introspect a known path even if there is no object on it if node && msg.interface == "org.freedesktop.DBus.Introspectable" && msg.member == "Introspect" reply = Message.new(Message::METHOD_RETURN).reply_to(msg) reply.sender = @unique_name xml = node.to_xml(msg.path) reply.add_param(Type::STRING, xml) @message_queue.push(reply) # dispatch for an object elsif node&.object node.object.dispatch(msg) else reply = Message.error(msg, "org.freedesktop.DBus.Error.UnknownObject", "Object #{msg.path} doesn't exist") @message_queue.push(reply) 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(msg) slot.call(msg) end end else # spec(Message Format): Unknown types must be ignored. DBus.logger.debug "Unknown message type: #{msg.}" end rescue Exception => e raise msg.annotate_exception(e) end |
#remove_match(match_rule) ⇒ void
This method returns an undefined value.
Returns actually return whether the rule existed, internal detail.
264 265 266 267 |
# File 'lib/dbus/connection.rb', line 264 def remove_match(match_rule) mrs = match_rule.to_s @signal_matchrules.delete(mrs).nil? end |
#request_service(name) ⇒ ObjectServer
Attempt to request a service name.
188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/dbus/connection.rb', line 188 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) handle_return_of_request_name(r, name) end object_server end |
#send_sync(msg, &retc) {|rmsg| ... } ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Send a message msg on to the bus. This is done synchronously, thus the call will block until a reply message arrives.
215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 |
# File 'lib/dbus/connection.rb', line 215 def send_sync(msg, &retc) # :yields: reply/return message return if msg.nil? # check if somethings wrong @message_queue.push(msg) @method_call_msgs[msg.serial] = msg @method_call_replies[msg.serial] = retc retm = return if retm.nil? # check if somethings wrong process(retm) while @method_call_replies.key? msg.serial retm = process(retm) end rescue EOFError new_err = DBus::Error.new("Connection dropped after we sent #{msg.inspect}") raise new_err end |
#send_sync_or_async(message, &reply_handler) ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
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
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/dbus/connection.rb', line 91 def send_sync_or_async(, &reply_handler) ret = nil if reply_handler.nil? send_sync() do |rmsg| raise rmsg if rmsg.is_a?(Error) ret = rmsg.params end else on_return() do |rmsg| if rmsg.is_a?(Error) reply_handler.call(rmsg) else reply_handler.call(rmsg, * rmsg.params) end end @message_queue.push() end ret end |
#wait_for_message ⇒ Object
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Wait for a message to arrive. Return it once it is available.
204 205 206 |
# File 'lib/dbus/connection.rb', line 204 def @message_queue.pop # FIXME: EOFError end |