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 205 def initialize(path) 206 @message_queue = MessageQueue.new(path) 207 @unique_name = nil 208 @method_call_replies = {} 209 @method_call_msgs = {} 210 @signal_matchrules = {} 211 @proxy = nil 212 @object_root = Node.new("/") 213 end
Public Instance Methods
Asks bus to send us messages matching mr, and execute slot when received
# File lib/dbus/bus.rb 480 def add_match(mr, &slot) 481 # check this is a signal. 482 mrs = mr.to_s 483 DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}" 484 # don't ask for the same match if we override it 485 unless @signal_matchrules.key?(mrs) 486 DBus.logger.debug "Asked for a new match" 487 proxy.AddMatch(mrs) 488 end 489 @signal_matchrules[mrs] = slot 490 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 218 def dispatch_message_queue 219 while (msg = @message_queue.pop(:non_block)) # FIXME: EOFError 220 process(msg) 221 end 222 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 569 def emit(service, obj, intf, sig, *args) 570 m = Message.new(DBus::Message::SIGNAL) 571 m.path = obj.path 572 m.interface = intf.name 573 m.member = sig.name 574 m.sender = service.name 575 i = 0 576 sig.params.each do |par| 577 m.add_param(par.type, args[i]) 578 i += 1 579 end 580 @message_queue.push(m) 581 end
Tell a bus to register itself on the glib main loop
# File lib/dbus/bus.rb 225 def glibize 226 require "glib2" 227 # Circumvent a ruby-glib bug 228 @channels ||= [] 229 230 gio = GLib::IOChannel.new(@message_queue.socket.fileno) 231 @channels << gio 232 gio.add_watch(GLib::IOChannel::IN) do |_c, _ch| 233 dispatch_message_queue 234 true 235 end 236 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 385 def introspect(dest, path) 386 if !block_given? 387 # introspect in synchronous ! 388 data = introspect_data(dest, path) 389 pof = DBus::ProxyObjectFactory.new(data, self, dest, path) 390 pof.build 391 else 392 introspect_data(dest, path) do |async_data| 393 yield(DBus::ProxyObjectFactory.new(async_data, self, dest, path).build) 394 end 395 end 396 end
@api private
# File lib/dbus/bus.rb 357 def introspect_data(dest, path, &reply_handler) 358 m = DBus::Message.new(DBus::Message::METHOD_CALL) 359 m.path = path 360 m.interface = "org.freedesktop.DBus.Introspectable" 361 m.destination = dest 362 m.member = "Introspect" 363 m.sender = unique_name 364 if reply_handler.nil? 365 send_sync_or_async(m).first 366 else 367 send_sync_or_async(m) do |*args| 368 # TODO: test async introspection, is it used at all? 369 args.shift # forget the message, pass only the text 370 reply_handler.call(*args) 371 nil 372 end 373 end 374 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 469 def on_return(m, &retc) 470 # Have a better exception here 471 if m.message_type != Message::METHOD_CALL 472 raise "on_return should only get method_calls" 473 end 474 @method_call_msgs[m.serial] = m 475 @method_call_replies[m.serial] = retc 476 end
@api private Process a message m based on its type.
# File lib/dbus/bus.rb 504 def process(m) 505 return if m.nil? # check if somethings wrong 506 case m.message_type 507 when Message::ERROR, Message::METHOD_RETURN 508 raise InvalidPacketException if m.reply_serial.nil? 509 mcs = @method_call_replies[m.reply_serial] 510 if !mcs 511 DBus.logger.debug "no return code for mcs: #{mcs.inspect} m: #{m.inspect}" 512 else 513 if m.message_type == Message::ERROR 514 mcs.call(Error.new(m)) 515 else 516 mcs.call(m) 517 end 518 @method_call_replies.delete(m.reply_serial) 519 @method_call_msgs.delete(m.reply_serial) 520 end 521 when DBus::Message::METHOD_CALL 522 if m.path == "/org/freedesktop/DBus" 523 DBus.logger.debug "Got method call on /org/freedesktop/DBus" 524 end 525 node = @service.get_node(m.path) 526 if !node 527 reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject", 528 "Object #{m.path} doesn't exist") 529 @message_queue.push(reply) 530 # handle introspectable as an exception: 531 elsif m.interface == "org.freedesktop.DBus.Introspectable" && 532 m.member == "Introspect" 533 reply = Message.new(Message::METHOD_RETURN).reply_to(m) 534 reply.sender = @unique_name 535 reply.add_param(Type::STRING, node.to_xml) 536 @message_queue.push(reply) 537 else 538 obj = node.object 539 return if obj.nil? # FIXME, pushes no reply 540 obj.dispatch(m) if obj 541 end 542 when DBus::Message::SIGNAL 543 # the signal can match multiple different rules 544 # clone to allow new signale handlers to be registered 545 @signal_matchrules.dup.each do |mrs, slot| 546 if DBus::MatchRule.new.from_s(mrs).match(m) 547 slot.call(m) 548 end 549 end 550 else 551 DBus.logger.debug "Unknown message type: #{m.message_type}" 552 end 553 rescue Exception => ex 554 raise m.annotate_exception(ex) 555 end
Set up a ProxyObject
for the bus itself, since the bus is introspectable. @return [ProxyObject] that always returns an array
({DBus::ApiOptions#proxy_method_returns_array})
Returns the object.
# File lib/dbus/bus.rb 425 def proxy 426 if @proxy.nil? 427 path = "/org/freedesktop/DBus" 428 dest = "org.freedesktop.DBus" 429 pof = DBus::ProxyObjectFactory.new( 430 DBUSXMLINTRO, self, dest, path, 431 api: ApiOptions::A0 432 ) 433 @proxy = pof.build["org.freedesktop.DBus"] 434 end 435 @proxy 436 end
# File lib/dbus/bus.rb 492 def remove_match(mr) 493 mrs = mr.to_s 494 rule_existed = @signal_matchrules.delete(mrs).nil? 495 # don't remove nonexisting matches. 496 return if rule_existed 497 # FIXME: if we do try, the Error.MatchRuleNotFound is *not* raised 498 # and instead is reported as "no return code for nil" 499 proxy.RemoveMatch(mrs) 500 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 407 def request_service(name) 408 # Use RequestName, but asynchronously! 409 # A synchronous call would not work with service activation, where 410 # method calls to be serviced arrive before the reply for RequestName 411 # (Ticket#29). 412 proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING) do |rmsg, r| 413 # check and report errors first 414 raise rmsg if rmsg.is_a?(Error) 415 raise NameRequestError unless r == REQUEST_NAME_REPLY_PRIMARY_OWNER 416 end 417 @service = Service.new(name, self) 418 @service 419 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 447 def send_sync(m, &retc) # :yields: reply/return message 448 return if m.nil? # check if somethings wrong 449 @message_queue.push(m) 450 @method_call_msgs[m.serial] = m 451 @method_call_replies[m.serial] = retc 452 453 retm = wait_for_message 454 return if retm.nil? # check if somethings wrong 455 456 process(retm) 457 while @method_call_replies.key? m.serial 458 retm = wait_for_message 459 process(retm) 460 end 461 rescue EOFError 462 new_err = DBus::Error.new("Connection dropped after we sent #{m.inspect}") 463 raise new_err 464 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 336 def send_sync_or_async(message, &reply_handler) 337 ret = nil 338 if reply_handler.nil? 339 send_sync(message) do |rmsg| 340 raise rmsg if rmsg.is_a?(Error) 341 ret = rmsg.params 342 end 343 else 344 on_return(message) do |rmsg| 345 if rmsg.is_a?(Error) 346 reply_handler.call(rmsg) 347 else 348 reply_handler.call(rmsg, * rmsg.params) 349 end 350 end 351 @message_queue.push(message) 352 end 353 ret 354 end
Retrieves the Service
with the given name. @return [Service]
# File lib/dbus/bus.rb 559 def service(name) 560 # The service might not exist at this time so we cannot really check 561 # anything 562 Service.new(name, self) 563 end
@api private Wait for a message to arrive. Return it once it is available.
# File lib/dbus/bus.rb 440 def wait_for_message 441 @message_queue.pop # FIXME: EOFError 442 end
Private Instance Methods
Send a hello messages to the bus to let it know we are here.
# File lib/dbus/bus.rb 587 def send_hello 588 m = Message.new(DBus::Message::METHOD_CALL) 589 m.path = "/org/freedesktop/DBus" 590 m.destination = "org.freedesktop.DBus" 591 m.interface = "org.freedesktop.DBus" 592 m.member = "Hello" 593 send_sync(m) do |rmsg| 594 @unique_name = rmsg.destination 595 DBus.logger.debug "Got hello reply. Our unique_name is #{@unique_name}" 596 end 597 @service = Service.new(@unique_name, self) 598 end