Class: DBus::Connection
- Inherits:
-
Object
- Object
- DBus::Connection
- Defined in:
- lib/dbus/bus.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
- DBUSXMLINTRO =
'<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> <node> <interface name="org.freedesktop.DBus.Introspectable"> <method name="Introspect"> <arg direction="out" type="s"/> </method> </interface> <interface name="org.freedesktop.DBus"> <method name="Hello"> <arg direction="out" type="s"/> </method> <method name="RequestName"> <arg direction="in" type="s"/> <arg direction="in" type="u"/> <arg direction="out" type="u"/> </method> <method name="ReleaseName"> <arg direction="in" type="s"/> <arg direction="out" type="u"/> </method> <method name="StartServiceByName"> <arg direction="in" type="s"/> <arg direction="in" type="u"/> <arg direction="out" type="u"/> </method> <method name="UpdateActivationEnvironment"> <arg direction="in" type="a{ss}"/> </method> <method name="NameHasOwner"> <arg direction="in" type="s"/> <arg direction="out" type="b"/> </method> <method name="ListNames"> <arg direction="out" type="as"/> </method> <method name="ListActivatableNames"> <arg direction="out" type="as"/> </method> <method name="AddMatch"> <arg direction="in" type="s"/> </method> <method name="RemoveMatch"> <arg direction="in" type="s"/> </method> <method name="GetNameOwner"> <arg direction="in" type="s"/> <arg direction="out" type="s"/> </method> <method name="ListQueuedOwners"> <arg direction="in" type="s"/> <arg direction="out" type="as"/> </method> <method name="GetConnectionUnixUser"> <arg direction="in" type="s"/> <arg direction="out" type="u"/> </method> <method name="GetConnectionUnixProcessID"> <arg direction="in" type="s"/> <arg direction="out" type="u"/> </method> <method name="GetAdtAuditSessionData"> <arg direction="in" type="s"/> <arg direction="out" type="ay"/> </method> <method name="GetConnectionSELinuxSecurityContext"> <arg direction="in" type="s"/> <arg direction="out" type="ay"/> </method> <method name="ReloadConfig"> </method> <method name="GetId"> <arg direction="out" type="s"/> </method> <method name="GetConnectionCredentials"> <arg direction="in" type="s"/> <arg direction="out" type="a{sv}"/> </method> <property name="Features" type="as" access="read"> <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> </property> <property name="Interfaces" type="as" access="read"> <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/> </property> <signal name="NameOwnerChanged"> <arg type="s"/> <arg type="s"/> <arg type="s"/> </signal> <signal name="NameLost"> <arg type="s"/> </signal> <signal name="NameAcquired"> <arg type="s"/> </signal> </interface> </node> '
Instance Attribute Summary collapse
-
#message_queue ⇒ Object
readonly
pop and push messages here.
-
#unique_name ⇒ Object
readonly
The unique name (by specification) of the message.
Instance Method Summary collapse
-
#add_match(match_rule, &slot) ⇒ Object
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.
-
#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
-
#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.
-
#proxy ⇒ ProxyObject
Set up a ProxyObject for the bus itself, since the bus is introspectable.
- #remove_match(match_rule) ⇒ Object
-
#request_service(name) ⇒ Service
Attempt to request a service name.
-
#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.
-
#service(name) ⇒ Service
(also: #[])
Retrieves the Service with the given name.
-
#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”
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/dbus/bus.rb', line 265 def initialize(path) @message_queue = MessageQueue.new(path) @unique_name = nil # @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 = {} @proxy = nil end |
Instance Attribute Details
#message_queue ⇒ Object (readonly)
pop and push messages here
257 258 259 |
# File 'lib/dbus/bus.rb', line 257 def @message_queue end |
#unique_name ⇒ Object (readonly)
The unique name (by specification) of the message.
255 256 257 |
# File 'lib/dbus/bus.rb', line 255 def unique_name @unique_name end |
Instance Method Details
#add_match(match_rule, &slot) ⇒ Object
Asks bus to send us messages matching mr, and execute slot when received
584 585 586 587 588 589 590 591 592 593 594 |
# File 'lib/dbus/bus.rb', line 584 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}" # don't ask for the same match if we override it unless @signal_matchrules.key?(mrs) DBus.logger.debug "Asked for a new match" proxy.AddMatch(mrs) end @signal_matchrules[mrs] = slot 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
285 286 287 288 289 |
# File 'lib/dbus/bus.rb', line 285 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.
685 686 687 688 689 690 691 692 693 694 695 696 697 |
# File 'lib/dbus/bus.rb', line 685 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 @message_queue.push(m) end |
#glibize ⇒ Object
Tell a bus to register itself on the glib main loop
292 293 294 295 296 297 298 299 300 301 302 303 |
# File 'lib/dbus/bus.rb', line 292 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 |
#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
473 474 475 476 477 478 479 480 481 482 483 484 |
# File 'lib/dbus/bus.rb', line 473 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.
445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/dbus/bus.rb', line 445 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 |
#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.
571 572 573 574 575 576 577 578 579 |
# File 'lib/dbus/bus.rb', line 571 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.
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 |
# File 'lib/dbus/bus.rb', line 611 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 = @service.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 |
#proxy ⇒ ProxyObject
Set up a ProxyObject for the bus itself, since the bus is introspectable. Returns the object.
521 522 523 524 525 526 527 528 529 530 531 532 |
# File 'lib/dbus/bus.rb', line 521 def proxy if @proxy.nil? path = "/org/freedesktop/DBus" dest = "org.freedesktop.DBus" pof = DBus::ProxyObjectFactory.new( DBUSXMLINTRO, self, dest, path, api: ApiOptions::A0 ) @proxy = pof.build["org.freedesktop.DBus"] end @proxy end |
#remove_match(match_rule) ⇒ Object
597 598 599 600 601 602 603 604 605 606 |
# File 'lib/dbus/bus.rb', line 597 def remove_match(match_rule) mrs = match_rule.to_s rule_existed = @signal_matchrules.delete(mrs).nil? # don't remove nonexisting matches. return if rule_existed # 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 |
#request_service(name) ⇒ Service
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.
495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 |
# File 'lib/dbus/bus.rb', line 495 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) details = if r == REQUEST_NAME_REPLY_IN_QUEUE other = proxy.GetNameOwner(name).first other_creds = proxy.GetConnectionCredentials(other).first "already owned by #{other}, #{other_creds.inspect}" else "error code #{r}" end raise NameRequestError, "Could not request #{name}, #{details}" unless r == REQUEST_NAME_REPLY_PRIMARY_OWNER end @service = Service.new(name, self) @service 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.
547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 |
# File 'lib/dbus/bus.rb', line 547 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
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 |
# File 'lib/dbus/bus.rb', line 423 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 |
#service(name) ⇒ Service Also known as: []
Retrieves the Service with the given name.
670 671 672 673 674 |
# File 'lib/dbus/bus.rb', line 670 def service(name) # The service might not exist at this time so we cannot really check # anything Service.new(name, self) 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.
536 537 538 |
# File 'lib/dbus/bus.rb', line 536 def @message_queue.pop # FIXME: EOFError end |