No way to advertise an interface?

Simon McVittie smcv at collabora.com
Thu Sep 30 13:30:41 UTC 2021


On Tue, 28 Sep 2021 at 21:27:21 +0200, Anders Feder wrote:
> [1]MPRIS defines a standard D-Bus interface for controlling media players. For
> clients to be able to discover services that implement the interface, the MPRIS
> specification requires each media player to request a unique bus name which
> begins with the prefix "org.mpris.MediaPlayer2".

This is a perfectly reasonable way to advertise that a program connected
to D-Bus implements a particular API specification.

I'm deliberately saying "specification" here because "interface" is D-Bus
jargon with a specific meaning, but I think it's what you mean here when
you say "interface" as a more general programming term. Implementing a
specified D-Bus interface on a specified object-path, either a constant
object path or one that can be mechanically derived from the bus name
used to advertise it, is an entirely reasonable API specification to have.

You're using the term "unique bus name" here in a way that is potentially
confusing, because "unique name", "unique bus name" or "unique connection
name" are interchangeable D-Bus jargon terms for the names that look
like ":1.42". The names that look like "com.example.Name" are called
*well-known bus names*.

A generalization of the pattern used in MPRIS looks something like this:

* every implementor must request ownership of a well-known bus name that
  starts with "com.example.MySpec."
* implementors are encouraged to choose the suffix after that fixed part
  based on some sufficiently-unique string, maybe their reversed DNS name
  or their app's human-facing name
* a subscriber interested in maintaining a list of implementors can
  add a match rule for the NameOwnerChanged signal, with
  arg0namespace="com.example.MySpec", and then call ListNames to get an
  initial list
* when it receives a NameOwnerChanged signal with first argument starting
  "com.example.MySpec.", the subscriber should update its list of
  implementations based on the information given by the second and third
  arguments (unique name of previous owner and new owner, respectively,
  or an empty string to indicate no owner)

For MPRIS, implementors are media players, and subscribers are anything
that wants to remote-control a media player. Replace these terms as
necessary for your particular protocol.

Joining the queue of owners of a shared name is also a reasonable
mechanism to advertise support for a particular API specification,
but lacks change-notification, unless the protocol designer and
all implementors are extremely careful. In the context of MPRIS2,
this is not the mechanism that was chosen when the specification was
written, so you cannot unilaterally choose to use it (although a future,
incompatible MPRIS3 protocol could use it, if people want that). In the
broader context of writing your own API specification, you're welcome
to use it if you'd prefer.

I think it's possible to get change-notification for the queue of owners of
a shared name if the protocol is defined to work like this:

* every implementation of the specification must request ownership of the
  shared name "com.example.MySpec", must use DBUS_NAME_FLAG_ALLOW_REPLACEMENT,
  and must not use DBUS_NAME_FLAG_DO_NOT_QUEUE
* a subscriber interested in maintaining a list of implementors should
  add a match rule for the NameOwnerChanged signal, with
  arg0="com.example.MySpec", and then call ListQueuedOwners to get an
  initial list
* when it receives a NameOwnerChanged signal with first argument
  "com.example.MySpec", the subscriber should call ListQueuedOwners again
  to refresh its list of implementations

but I haven't tested that or put much thought into it. It's a lot more
subtle than the pattern that uses prefixes (because if one implementation
gets its flags wrong, it will break the protocol for everyone by remaining
at the head of the queue), and it requires responding to NameOwnerChanged
by polling ListQueuedOwners, instead of already having the necessary
information in the signal arguments (there is no signal to indicate that
a non-primary owner of a name has left the queue).

> This to me seems like bad practice: The whole purpose of interface namespaces
> is to prevent naming collisions. This is supposed to be accomplished by the
> notional owner of the namespace controlling which names are minted in the
> namespace. But the MPRIS standard relinquishes this control and tacitly gives
> anyone permission to take whatever name they want in the
> "org.mpris.MediaPlayer2" namespace, without any control for naming collisions.

That's the MPRIS namespace owner's choice to make.

If they are reasonably confident that name collisions in their namespace
will not, in practice, happen, then they are free to suggest using a
simpler/shorter name in their namespace. I suspect that the reason MPRIS
does this is that most people do not install a very large number of media
players, and if they do, they probably stick to installing media players
with distinct names. Having two media players with the same human-readable
name would be a significant usability problem already (how do you know
which one to click on in a menu?), so if the names used in the MPRIS
namespace are closely related to the human-readable names, in practice
either they will not collide or you already have more serious problems.

If they are concerned about collisions, then they are also free to
suggest nesting a second reversed domain name inside their namespace,
for example org.mpris.MediaPlayer2.org.videolan.VLC. If their protocol
suggests short/non-namespaced names (like MPRIS does) but allows multiple
bus-name components after the required prefix (like MPRIS does, but
unlike for example Telepathy, which does not allow dots in connection
manager names for historical reasons) then you can even choose to use
a second reversed domain name unilaterally, without asking anyone's
permission or breaking interoperability. D-Bus well-known names can be
up to 255 characters, so you have quite a lot of space for namespacing.

The wider view here is that any advisory protocol for how to use a shared
resource relies on everyone using that shared resource cooperatively.
The reversed-domain-name convention is just that: a convention. There is
no D-Bus police that will penalize you for using an inappropriate bus name,
and nobody is in a position to enforce that your application does not
impersonate some other application (except for sandboxed app frameworks
like Flatpak, which can use technical measures to prevent an application
from advertising or obtaining names that it did not declare in its app
metadata).

If a name collision occurs, the way to resolve it is reasonably simple:
don't install more than one of the colliding applications, maybe open a bug,
and (assuming open source) maybe patch one or both so that they stop
colliding. This is equally true regardless of whether the colliding names
are foobar or com.collabora.FooBar (choosing collabora.com as an arbitrary
example of a domain name you don't own).

This is basically the same way you would respond to any other
quality-of-implementation issue, such as a crash, a name collision
in /usr/bin, non-compliance with some spec like D-Bus or MPRIS, or a
missing feature: open a bug, stop using the app that has the issue if
the issue is bad enough to make it useless to you, and/or fix it yourself
by patching the source code.

The *only* thing the reversed-domain-name convention gives us is a simple
way to say, if a collision occurs, what the social convention is for
which app is expected to take responsibility for renaming. If the designers
of MPRIS think their ecosystem is small enough not to need this social
convention, that's their decision to make.

> Is there no better way to implement this kind of discoverability on D-Bus?

Because D-Bus is an OS-level service and the version shipped in a LTS
operating system might be several years old, in general the lower-level
protocols are designed to be powerful enough that they can be used to
implement multiple higher-level patterns without asking permission.

This means that when the authors of specifications like MPRIS
want to do something in their specification, they don't need
to change the lower-level D-Bus Specification, change all the
(multiple!) implementations, get maintainer review on all those changes,
and wait for all relevant distributions to upgrade to a new enough
version of each implementation. Instead, they can write their specification
in terms of existing D-Bus features, then start using it immediately.

For example, name ownership does a lot of work: you might assume
that name ownership is a service discovery mechanism, but it's also a
distributed mutex (as in the audio device reservation protocol originating
in PulseAudio), and also a way to have single-instance applications
(replacing libunique), and it can also be a feature-discovery mechanism
(as in this thread). This is not a bug, it's a feature! The dbus-daemon
doesn't need to know what you are using name ownership to achieve,
it just needs to implement the spec as documented.

Another example of this philosophy is that org.freedesktop.DBus.Properties
could have been designed with special message types GET_PROPERTY,
SET_PROPERTY and PROPERTY_CHANGE_NOTIFICATION alongside METHOD_CALL and
SIGNAL, but it wasn't. Instead, it's just a convention for how to use
the existing METHOD_CALL and SIGNAL messages (and in fact the reference
implementation, libdbus, has no specific code to support properties).

    smcv


More information about the dbus mailing list