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
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
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 477 def add_match(mr, &slot) 478 # check this is a signal. 479 mrs = mr.to_s 480 DBus.logger.debug "#{@signal_matchrules.size} rules, adding #{mrs.inspect}" 481 # don't ask for the same match if we override it 482 unless @signal_matchrules.key?(mrs) 483 DBus.logger.debug "Asked for a new match" 484 proxy.AddMatch(mrs) 485 end 486 @signal_matchrules[mrs] = slot 487 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 566 def emit(service, obj, intf, sig, *args) 567 m = Message.new(DBus::Message::SIGNAL) 568 m.path = obj.path 569 m.interface = intf.name 570 m.member = sig.name 571 m.sender = service.name 572 i = 0 573 sig.params.each do |par| 574 m.add_param(par.type, args[i]) 575 i += 1 576 end 577 @message_queue.push(m) 578 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 466 def on_return(m, &retc) 467 # Have a better exception here 468 if m.message_type != Message::METHOD_CALL 469 raise "on_return should only get method_calls" 470 end 471 @method_call_msgs[m.serial] = m 472 @method_call_replies[m.serial] = retc 473 end
@api private Process a message m based on its type.
# File lib/dbus/bus.rb 501 def process(m) 502 return if m.nil? # check if somethings wrong 503 case m.message_type 504 when Message::ERROR, Message::METHOD_RETURN 505 raise InvalidPacketException if m.reply_serial.nil? 506 mcs = @method_call_replies[m.reply_serial] 507 if !mcs 508 DBus.logger.debug "no return code for mcs: #{mcs.inspect} m: #{m.inspect}" 509 else 510 if m.message_type == Message::ERROR 511 mcs.call(Error.new(m)) 512 else 513 mcs.call(m) 514 end 515 @method_call_replies.delete(m.reply_serial) 516 @method_call_msgs.delete(m.reply_serial) 517 end 518 when DBus::Message::METHOD_CALL 519 if m.path == "/org/freedesktop/DBus" 520 DBus.logger.debug "Got method call on /org/freedesktop/DBus" 521 end 522 node = @service.get_node(m.path) 523 if !node 524 reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject", 525 "Object #{m.path} doesn't exist") 526 @message_queue.push(reply) 527 # handle introspectable as an exception: 528 elsif m.interface == "org.freedesktop.DBus.Introspectable" && 529 m.member == "Introspect" 530 reply = Message.new(Message::METHOD_RETURN).reply_to(m) 531 reply.sender = @unique_name 532 reply.add_param(Type::STRING, node.to_xml) 533 @message_queue.push(reply) 534 else 535 obj = node.object 536 return if obj.nil? # FIXME, pushes no reply 537 obj.dispatch(m) if obj 538 end 539 when DBus::Message::SIGNAL 540 # the signal can match multiple different rules 541 # clone to allow new signale handlers to be registered 542 @signal_matchrules.dup.each do |mrs, slot| 543 if DBus::MatchRule.new.from_s(mrs).match(m) 544 slot.call(m) 545 end 546 end 547 else 548 DBus.logger.debug "Unknown message type: #{m.message_type}" 549 end 550 rescue Exception => ex 551 raise m.annotate_exception(ex) 552 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 489 def remove_match(mr) 490 mrs = mr.to_s 491 rule_existed = @signal_matchrules.delete(mrs).nil? 492 # don't remove nonexisting matches. 493 return if rule_existed 494 # FIXME: if we do try, the Error.MatchRuleNotFound is *not* raised 495 # and instead is reported as "no return code for nil" 496 proxy.RemoveMatch(mrs) 497 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 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 556 def service(name) 557 # The service might not exist at this time so we cannot really check 558 # anything 559 Service.new(name, self) 560 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 584 def send_hello 585 m = Message.new(DBus::Message::METHOD_CALL) 586 m.path = "/org/freedesktop/DBus" 587 m.destination = "org.freedesktop.DBus" 588 m.interface = "org.freedesktop.DBus" 589 m.member = "Hello" 590 send_sync(m) do |rmsg| 591 @unique_name = rmsg.destination 592 DBus.logger.debug "Got hello reply. Our unique_name is #{@unique_name}" 593 end 594 @service = Service.new(@unique_name, self) 595 end