How to Version D-Bus Interfaces Properly and Why Using / as Service Entry Point Sucks

Post Syndicated from Lennart Poettering original https://0pointer.net/blog/projects/versioning-dbus.html

So you are designing a D-Bus interface and want to make it future-proof. Of
course, you thought about versioning your stuff. But you wonder how to do that
best. Here are a few things I learned about versioning D-Bus APIs which might
be of general interest:

Version your interfaces! This one is pretty obvious. No explanation
needed. Simply include the interface version in the interface name as suffix.
i.e. the initial release should use org.foobar.AwesomeStuff1, and if
you do changes you should introduce org.foobar.AwesomeStuff2, and so
on, possibly dropping the old interface.

When should you bump the interface version? Generally, I’d recommend only
bumping when doing incompatible changes, such as function call signature
changes. This of course requires clients to handle the
org.freedesktop.DBus.Error.UnknownMethod error properly for each function
you add to an existing interface. That said, in a few cases it might make sense
to bump the interface version even without breaking compatibility of the calls.
(e.g. in case you add something to an interface that is not directly visible in
the introspection data)

Version your services! This one is almost as obvious. When you
completely rework your D-Bus API introducing a new service name might be a
good idea. Best way to do this is by simply bumping the service name. Hence,
call your service org.foobar.AwesomeService1 right from the beginning
and then bump the version if you reinvent the wheel. And don’t forget that you
can acquire more than one well-known service name on the bus, so even if you
rework everything you can keep compatibilty. (Example: BlueZ 3 to BlueZ 4 switch)

Version your ‘entry point’ object paths! This one is far from
obvious. The reasons why object paths should be versioned are purely technical,
not philosophical: for signals sent from a service D-Bus overwrites the
originating service name by the unique name (e.g. :1.42) even if you
fill in a well-known name (e.g. org.foobar.AwesomeService1). Now,
let’s say your application registers two well-known service names, let’s say
two versions of the same service, versioned like mentioned above. And you have
two objects — one on each of the two service names — that implement a generic
interface and share the same object path: for the client there will be no way
to figure out to which service name the signals sent from this object path
belong. And that’s why you should make sure to use versioned and hence
different paths for both objects. i.e. start with
/org/foobar/AwesomeStuff1 and then bump to
/org/foobar/AwesomeStuff2 and so on. (Also see David’s comments about this.)

When should you bump the object path version? Probably only when you
bump the service name it belongs to. Important is to version the ‘entry point’
object path. Objects below that don’t need explicit versioning.

In summary: For good D-Bus API design you should version all three: D-Bus interfaces, service names and ‘entry point’ object paths.

And don’t forget: nobody gets API design right the first time. So even if
you think your D-Bus API is perfect: version things right from the beginning
because later on it might turn out you were not quite as bright as you thought
you were.

A corollary from the reasoning behind versioning object paths as described
above is that using / as entry point object path for your service is a
bad idea. It makes it very hard to implement more than one service or service
version on a single D-Bus connection. Again: Don’t use / as entry
point object path. Use something like /org/foobar/AwesomeStuff!