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 name="data" direction="out" type="s"/> </method> </interface> <interface name="org.freedesktop.DBus"> <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="Hello"> <arg direction="out" type="s"/> </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="GetConnectionSELinuxSecurityContext"> <arg direction="in" type="s"/> <arg direction="out" type="ay"/> </method> <method name="ReloadConfig"> </method> <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(mr, &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.
- #eventmachinize ⇒ Object
-
#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(m, &retc) ⇒ Object
private
Specify a code block that has to be executed when a reply for message m is received.
-
#process(m) ⇒ Object
private
Process a message m based on its type.
-
#proxy ⇒ Object
Set up a ProxyObject for the bus itself, since the bus is introspectable.
- #remove_match(mr) ⇒ Object
-
#request_service(name) ⇒ Service
Attempt to request a service name.
-
#send_sync(m, &retc) ⇒ Object
private
Send a message m 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”
198 199 200 201 202 203 204 205 206 |
# File 'lib/dbus/bus.rb', line 198 def initialize(path) = MessageQueue.new(path) @unique_name = nil @method_call_replies = Hash.new @method_call_msgs = Hash.new @signal_matchrules = Hash.new @proxy = nil @object_root = Node.new("/") end |
Instance Attribute Details
#message_queue ⇒ Object (readonly)
pop and push messages here
190 191 192 |
# File 'lib/dbus/bus.rb', line 190 def end |
#unique_name ⇒ Object (readonly)
The unique name (by specification) of the message.
188 189 190 |
# File 'lib/dbus/bus.rb', line 188 def unique_name @unique_name end |
Instance Method Details
#add_match(mr, &slot) ⇒ Object
Asks bus to send us messages matching mr, and execute slot when received
478 479 480 481 482 483 484 485 486 487 488 |
# File 'lib/dbus/bus.rb', line 478 def add_match(mr, &slot) # check this is a signal. mrs = mr.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
211 212 213 214 215 |
# File 'lib/dbus/bus.rb', line 211 def while (msg = .pop(:non_block)) # 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.
566 567 568 569 570 571 572 573 574 575 576 577 578 |
# File 'lib/dbus/bus.rb', line 566 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 .push(m) end |
#eventmachinize ⇒ Object
232 233 234 235 236 237 |
# File 'lib/dbus/bus.rb', line 232 def eventmachinize require File.join(File.dirname(File.(__FILE__)), "loop-em") conn = ::EventMachine.watch(.socket, Loop::EventMachine::Reader, self) conn.notify_readable = true end |
#glibize ⇒ Object
Tell a bus to register itself on the glib main loop
218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/dbus/bus.rb', line 218 def glibize require 'glib2' # Circumvent a ruby-glib bug @channels ||= Array.new gio = GLib::IOChannel.new(.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
389 390 391 392 393 394 395 396 397 398 399 400 |
# File 'lib/dbus/bus.rb', line 389 def introspect(dest, path) if not block_given? # introspect in synchronous ! data = introspect_data(dest, path) pof = DBus::ProxyObjectFactory.new(data, self, dest, path) return 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.
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
# File 'lib/dbus/bus.rb', line 361 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(m, &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 m is received.
467 468 469 470 471 472 473 474 |
# File 'lib/dbus/bus.rb', line 467 def on_return(m, &retc) # Have a better exception here if m. != Message::METHOD_CALL raise "on_return should only get method_calls" end @method_call_msgs[m.serial] = m @method_call_replies[m.serial] = retc end |
#process(m) ⇒ 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 m based on its type.
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 |
# File 'lib/dbus/bus.rb', line 502 def process(m) return if m.nil? #check if somethings wrong case m. when Message::ERROR, Message::METHOD_RETURN raise InvalidPacketException if m.reply_serial == nil mcs = @method_call_replies[m.reply_serial] if not mcs DBus.logger.debug "no return code for mcs: #{mcs.inspect} m: #{m.inspect}" else if m. == Message::ERROR mcs.call(Error.new(m)) else mcs.call(m) end @method_call_replies.delete(m.reply_serial) @method_call_msgs.delete(m.reply_serial) end when DBus::Message::METHOD_CALL if m.path == "/org/freedesktop/DBus" DBus.logger.debug "Got method call on /org/freedesktop/DBus" end node = @service.get_node(m.path) if not node reply = Message.error(m, "org.freedesktop.DBus.Error.UnknownObject", "Object #{m.path} doesn't exist") .push(reply) # handle introspectable as an exception: elsif m.interface == "org.freedesktop.DBus.Introspectable" and m.member == "Introspect" reply = Message.new(Message::METHOD_RETURN).reply_to(m) reply.sender = @unique_name reply.add_param(Type::STRING, node.to_xml) .push(reply) else obj = node.object return if obj.nil? # FIXME, pushes no reply obj.dispatch(m) if obj end when DBus::Message::SIGNAL # the signal can match multiple different rules @signal_matchrules.each do |mrs, slot| if DBus::MatchRule.new.from_s(mrs).match(m) slot.call(m) end end else DBus.logger.debug "Unknown message type: #{m.message_type}" end rescue Exception => ex raise m.annotate_exception(ex) end |
#proxy ⇒ Object
Set up a ProxyObject for the bus itself, since the bus is introspectable. Returns the object.
429 430 431 432 433 434 435 436 437 |
# File 'lib/dbus/bus.rb', line 429 def proxy if @proxy == nil path = "/org/freedesktop/DBus" dest = "org.freedesktop.DBus" pof = DBus::ProxyObjectFactory.new(DBUSXMLINTRO, self, dest, path) @proxy = pof.build["org.freedesktop.DBus"] end @proxy end |
#remove_match(mr) ⇒ Object
490 491 492 493 494 495 496 497 498 |
# File 'lib/dbus/bus.rb', line 490 def remove_match(mr) mrs = mr.to_s unless @signal_matchrules.delete(mrs).nil? # don't remove nonexisting matches. # 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 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.
411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 |
# File 'lib/dbus/bus.rb', line 411 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| if rmsg.is_a?(Error) # check and report errors first raise rmsg elsif r != REQUEST_NAME_REPLY_PRIMARY_OWNER raise NameRequestError end end @service = Service.new(name, self) @service end |
#send_sync(m, &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.
Send a message m on to the bus. This is done synchronously, thus the call will block until a reply message arrives.
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 |
# File 'lib/dbus/bus.rb', line 448 def send_sync(m, &retc) # :yields: reply/return message return if m.nil? #check if somethings wrong .push(m) @method_call_msgs[m.serial] = m @method_call_replies[m.serial] = retc retm = return if retm.nil? #check if somethings wrong process(retm) while @method_call_replies.has_key? m.serial retm = process(retm) end 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
337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
# File 'lib/dbus/bus.rb', line 337 def send_sync_or_async(, &reply_handler) ret = nil if reply_handler.nil? send_sync() do |rmsg| if rmsg.is_a?(Error) raise rmsg else ret = rmsg.params end end else on_return() do |rmsg| if rmsg.is_a?(Error) reply_handler.call(rmsg) else reply_handler.call(rmsg, * rmsg.params) end end .push() end ret end |
#service(name) ⇒ Service Also known as: []
Retrieves the Service with the given name.
556 557 558 559 560 |
# File 'lib/dbus/bus.rb', line 556 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.
441 442 443 |
# File 'lib/dbus/bus.rb', line 441 def .pop # FIXME EOFError end |