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

@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
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
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
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 rescue EOFError
462   new_err = DBus::Error.new("Connection dropped after we sent #{m.inspect}")
463   raise new_err
464 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
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
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
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