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

message_queue[R]

pop and push messages here

unique_name[R]

The unique name (by specification) of the message.

Public Class Methods

new(path) click to toggle source

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

[](name)
Alias for: service
add_match(mr, &slot) click to toggle source

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_message_queue() click to toggle source

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
emit(service, obj, intf, sig, *args) click to toggle source

@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
glibize() click to toggle source

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
introspect(dest, path) { |proxy_object_factory.build| ... } click to toggle source

@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
introspect_data(dest, path, &reply_handler) click to toggle source

@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
on_return(m, &retc) click to toggle source

@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
process(m) click to toggle source

@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
proxy() click to toggle source

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
remove_match(mr) click to toggle source
    # 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
request_service(name) click to toggle source

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
send_sync(m) { |reply| ... } click to toggle source

@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
send_sync_or_async(message, &reply_handler) click to toggle source

@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
service(name) click to toggle source

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
Also aliased as: []
wait_for_message() click to toggle source

@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_hello() click to toggle source

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