DBus Specification questions

Simon McVittie smcv at collabora.com
Tue Mar 31 10:33:26 UTC 2020


On Mon, 30 Mar 2020 at 23:07:02 -0400, Robert Middleton wrote:
> I'm working on updating some DBus bindings, and part of that effort
> involves reimplementing libdbus.

The terminology that we normally use is that if you wrap libdbus (or maybe
some other implementation like GDBus or sd-bus), you're writing a binding;
but if you're reimplementing all the bits of libdbus you previously used,
your library has moved from being a binding to being an implementation.

So, for example, dbus-python and dbus-glib are bindings (they use
libdbus to do the real work), but GDBus and sd-bus are implementations
(they implement the whole D-Bus message framing protocol over a Unix or
TCP socket themselves).

Changing your approach is fine - I think dbus-java 1.x was a binding and
2.x is an implementation? - but if you use the same terms as everyone
else it'll reduce confusion.

Writing an implementation is more work than writing a binding, but might
give you better results in the end, because libdbus is "all things to
all people" and is not necessarily particularly great for any single use.

Some design principles that might answer most of your more specific
questions below:

* Be conservative/strict in what you emit
* Be somewhat liberal in what you accept, but only as far as is convenient
* Return exactly the number of replies that are expected (which is either
  0 or 1)
* Don't be remotely-crashable

> 1. Is it possible to have multiple interfaces with the same name on
> the same path?

If you do, it's a bug: an interface is meant to have the same methods in
it every time (possibly adding new methods in new versions) - it's like
a C library API, or a C++ or Java class - so any given object should
only be able to have one interface of a particular name. On the service
side, you shouldn't design your library to make this possible.

On the client side, how/whether you cope with such services is a
quality-of-implementation thing, and I'd say it's undefined from the spec
point of view. The reasonable interpretations are probably: use the first,
use the last, or merge interfaces of the same name into one big interface.

> 2. Even though it is valid to have a METHOD_CALL without an interface,
> is it valid to disregard messages that don't have an interface, or
> should an effort be made to do something with the message?  Would it
> be valid to return an error in this case?

You shouldn't just not reply (assuming the NO_REPLY_EXPECTED flag is not
set). You can either try to match the method name to a method of that
name in one of your interfaces, or return an error:

     In the absence of an INTERFACE field, if two or more interfaces on
     the same object have a method with the same name, it is undefined
     which of those methods will be invoked. Implementations may choose
     to either return an error, or deliver the message as though it had
     an arbitrary one of those interfaces.

So as a concrete example, if an object path in your service implements
com.example.Foo1.Jump and org.example.Bar2.Jump, and a client calls
Jump() without specifying an interface, I would consider any of these to
be equally valid:

* always behave as though it was ...Foo1.Jump
* always behave as though it was ...Bar2.Jump
* pick one according to some heuristic, like trying to match the
  argument types
* return a standard error like org.freedesktop.DBus.Error.UnknownMethod
* return your own non-standard net.example.YourBinding.AmbiguousMethod
  error

I also think it would be valid to respond to *all* no-interface method
calls with an error, even if they are unambiguous - although you might
find that you get bug reports from people whose clients are being
insufficiently careful. For services, this is a quality-of-implementation
thing, rather than conformance; a high-quality client should always know
(and specify) which interface it is trying to use.

> 3. Who processes METHOD_CALL timeouts?  From my use of dbus-java I
> believe that this is always handled by the sender, but the
> specification does not mention anything about it.

I don't think the specification mentions timeouts at all - which means
that if you want a timeout (you do), it more or less *has to be* handled
by the sender. If it was handled by anyone else, they would have no way
to know what timeout the sender considered to be appropriate, because
there's no encoding for "the timeout is..." in a message header.

The reference dbus-daemon can theoretically impose a second layer of
timeouts (orthogonal to the client-side timeouts!) on all messages that
go through it, but in practice that timeout has been set to "infinite"
since 2009.

> 4. Do any sort of compliance checks exist at the moment?  I want to to
> try and define as many unit tests as possible to ensure that erroneous
> conditions are properly dealt with / invalid data doesn't get sent.

No, I don't think there's a generic compliance check. The "cross-test"
that is implemented by at least dbus-python and dbus-java is probably
the closest thing there is, but it isn't amazingly well-defined, and is
mostly implemented by dead or semi-dead projects.

> 5. If a call has the NO_REPLY_EXPECTED flag set, does sending a
> response or an error back constitute an invalid action?

In RFC 2119 terms, I'd say it's a SHOULD NOT. Peers are expected to not
crash or misbehave or anything like that if you do send an "unsolicited
reply", but you'll get warnings logged: for example, on the well-known
system bus, replying to a NO_REPLY_EXPECTED message causes security policy
warnings to be logged, because the dbus-daemon keeps a record of "exactly
one reply to this is allowed" for ordinary method calls, and does not do
that for NO_REPLY_EXPECTED method calls. So the unsolicited reply is
rejected, with a warning.

Note that non-method-calls (signals, successful replies, error replies)
always behave as though they are NO_REPLY_EXPECTED, even if the flag
isn't set.

    smcv


More information about the dbus mailing list