From d83598b3307a12795240b7c5ce6233a62849daba Mon Sep 17 00:00:00 2001 From: Philip Withnall Date: Thu, 5 Feb 2015 12:08:03 +0000 Subject: [PATCH] doc: Add a guide to designing D-Bus APIs This guide gives some pointers on how to write D-Bus APIs which are nice to use. It adds an optional dependency on Ducktype and yelp-build from yelp-tools. These are used when available, but are not required unless --enable-ducktype-docs is passed to configure. They are required for uploading the docs, however. Bug: https://bugs.freedesktop.org/show_bug.cgi?id=88994 --- Notes: This version of the document is complete. There is one FIXME left for the future about mentioning kdbus and the new GVariant-style type system, but that should not be addressed until kdbus has landed and stabilised. Any integration with the CMake build system still remains to be done. configure.ac | 39 ++- doc/.gitignore | 9 + doc/Makefile.am | 35 ++- doc/dbus-api-design.duck | 788 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 867 insertions(+), 4 deletions(-) create mode 100644 doc/dbus-api-design.duck diff --git a/configure.ac b/configure.ac index 1358510..7812b48 100644 --- a/configure.ac +++ b/configure.ac @@ -150,6 +150,7 @@ AC_ARG_ENABLE(asserts, AS_HELP_STRING([--enable-asserts],[include assertion chec AC_ARG_ENABLE(checks, AS_HELP_STRING([--enable-checks],[include sanity checks on public API]),enable_checks=$enableval,enable_checks=yes) AC_ARG_ENABLE(xml-docs, AS_HELP_STRING([--enable-xml-docs],[build XML documentation (requires xmlto)]),enable_xml_docs=$enableval,enable_xml_docs=auto) AC_ARG_ENABLE(doxygen-docs, AS_HELP_STRING([--enable-doxygen-docs],[build DOXYGEN documentation (requires Doxygen)]),enable_doxygen_docs=$enableval,enable_doxygen_docs=auto) +AC_ARG_ENABLE(ducktype-docs, AS_HELP_STRING([--enable-ducktype-docs],[build Ducktype documentation (requires Ducktype)]),enable_ducktype_docs=$enableval,enable_ducktype_docs=auto) AC_ARG_ENABLE(abstract-sockets, AS_HELP_STRING([--enable-abstract-sockets],[use abstract socket namespace (linux only)]),enable_abstract_sockets=$enableval,enable_abstract_sockets=auto) AC_ARG_ENABLE(selinux, AS_HELP_STRING([--enable-selinux],[build with SELinux support]),enable_selinux=$enableval,enable_selinux=auto) AC_ARG_ENABLE(libaudit,AS_HELP_STRING([--enable-libaudit],[build audit daemon support for SELinux]),enable_libaudit=$enableval,enable_libaudit=auto) @@ -1421,6 +1422,36 @@ AC_MSG_RESULT($enable_doxygen_docs) AC_CHECK_PROGS([XSLTPROC], [xsltproc]) AM_CONDITIONAL([DBUS_HAVE_XSLTPROC], [test "x$XSLTPROC" != "x"]) +### Ducktype/Yelp documentation + +AC_PATH_PROG([DUCKTYPE],[ducktype],[no]) +AC_PATH_PROG([YELP_BUILD],[yelp-build],[no]) + +AC_MSG_CHECKING([whether to build Ducktype documentation]) + +AS_IF([test "$DUCKTYPE" = "no"],[have_ducktype=no],[have_ducktype=yes]) +AS_IF([test "$YELP_BUILD" = "no"],[have_yelp_build=no],[have_yelp_build=yes]) + +AS_IF([test "$enable_ducktype_docs" = "auto"],[ + AS_IF([test "$have_ducktype" = "no" -o "$have_yelp_build" = "no"],[ + enable_ducktype_docs=no + ],[ + enable_ducktype_docs=yes + ]) +]) + +AS_IF([test "$enable_ducktype_docs" = "yes"],[ + AS_IF([test "$have_ducktype" = "no"],[ + AC_MSG_ERROR([Building Ducktype docs explicitly required, but ducktype not found]) + ]) + AS_IF([test "$have_yelp_build" = "no"],[ + AC_MSG_ERROR([Building Ducktype docs explicitly required, but yelp-build not found]) + ]) +]) + +AM_CONDITIONAL([DBUS_DUCKTYPE_DOCS_ENABLED],[test "$enable_ducktype_docs" = "yes"]) +AC_MSG_RESULT([$enable_ducktype_docs]) + ### XML Documentation AC_PATH_PROG(XMLTO, xmlto, no) @@ -1451,7 +1482,8 @@ AM_CONDITIONAL(DBUS_XML_DOCS_ENABLED, test x$enable_xml_docs = xyes) AC_MSG_RESULT($enable_xml_docs) AM_CONDITIONAL(DBUS_CAN_UPLOAD_DOCS, - [test x$enable_doxygen_docs = xyes && test x$enable_xml_docs = xyes]) + [test x$enable_doxygen_docs = xyes && test x$enable_xml_docs = xyes && + test x$enable_ducktype_docs = xyes]) #### Have to go $localstatedir->$prefix/var->/usr/local/var @@ -1817,7 +1849,9 @@ echo " 32-bit int: ${DBUS_INT32_TYPE} 16-bit int: ${DBUS_INT16_TYPE} Doxygen: ${DOXYGEN:-not found} - xmlto: ${XMLTO:-not found}" + xmlto: ${XMLTO:-not found} + ducktype: ${DUCKTYPE:-not found} + yelp-build: ${YELP_BUILD:-not found}" echo " Rebuilding generated files: ${USE_MAINTAINER_MODE} @@ -1837,6 +1871,7 @@ echo " Building systemd support: ${have_systemd} Building X11 code: ${have_x11} Building Doxygen docs: ${enable_doxygen_docs} + Building Ducktype docs: ${enable_ducktype_docs} Building XML docs: ${enable_xml_docs} Building launchd support: ${have_launchd} Init scripts style: ${with_init_scripts} diff --git a/doc/.gitignore b/doc/.gitignore index 708fe36..e1ccbc8 100644 --- a/doc/.gitignore +++ b/doc/.gitignore @@ -17,3 +17,12 @@ dbus-faq.html dbus-docs dbus-docs.tar.gz doxygen.stamp +dbus-api-design.page +dbus-api-design.html +jquery.js +jquery.syntax.brush.html.js +jquery.syntax.core.js +jquery.syntax.js +jquery.syntax.layout.yelp.js +yelp.js +C.css diff --git a/doc/Makefile.am b/doc/Makefile.am index b9a4c10..521ea4f 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -30,6 +30,7 @@ STATIC_DOCS = \ dbus-specification.xml \ dbus-test-plan.xml \ dbus-tutorial.xml \ + dbus-api-design.duck \ dcop-howto.txt \ introspect.xsl \ $(DTDS) @@ -50,8 +51,24 @@ STATIC_HTML = \ diagram.svg \ $(NULL) +# Static HTML helper files generated by yelp-build. +YELP_STATIC_HTML = \ + yelp.js \ + C.css \ + jquery.js \ + jquery.syntax.js \ + jquery.syntax.brush.html.js \ + jquery.syntax.core.js \ + jquery.syntax.layout.yelp.js \ + $(NULL) + dist_html_DATA += $(STATIC_HTML) +# Content HTML files generated by yelp-build. +YELP_HTML = \ + dbus-api-design.html \ + $(NULL) + XMLTO_HTML = \ dbus-faq.html \ dbus-specification.html \ @@ -84,6 +101,16 @@ dbus.devhelp: $(srcdir)/doxygen_to_devhelp.xsl doxygen.stamp $(XSLTPROC) -o $@ $< api/xml/index.xml endif +if DBUS_DUCKTYPE_DOCS_ENABLED +html_DATA += $(YELP_HTML) $(YELP_STATIC_HTML) + +%.page: %.duck + $(DUCKTYPE) $< +%.html: %.page + $(YELP_BUILD) html $< +$(YELP_STATIC_HTML): $(YELP_HTML) +endif + # this assumes CREATE_SUBDIRS isn't set to YES in Doxyfile # (which it isn't currently) install-data-local:: doxygen.stamp @@ -112,13 +139,15 @@ BONUS_FILES = \ $(top_srcdir)/COPYING \ $(top_srcdir)/ChangeLog -dbus-docs: $(STATIC_DOCS) $(MAN_XML_FILES) $(dist_doc_DATA) $(dist_html_DATA) $(MAN_HTML_FILES) $(BONUS_FILES) doxygen.stamp $(XMLTO_HTML) +dbus-docs: $(STATIC_DOCS) $(MAN_XML_FILES) $(dist_doc_DATA) $(dist_html_DATA) $(MAN_HTML_FILES) $(BONUS_FILES) doxygen.stamp $(XMLTO_HTML) $(YELP_HTML) $(YELP_STATIC_HTML) $(AM_V_at)rm -rf $@ $@.tmp $(AM_V_GEN)$(MKDIR_P) $@.tmp/api $(AM_V_at)cd $(srcdir) && cp $(STATIC_DOCS) @abs_builddir@/$@.tmp $(AM_V_at)cd $(srcdir) && cp $(dist_doc_DATA) @abs_builddir@/$@.tmp $(AM_V_at)cd $(srcdir) && cp $(STATIC_HTML) @abs_builddir@/$@.tmp $(AM_V_at)cp $(XMLTO_HTML) @abs_builddir@/$@.tmp + $(AM_V_at)cp $(YELP_HTML) @abs_builddir@/$@.tmp + $(AM_V_at)cp $(YELP_STATIC_HTML) @abs_builddir@/$@.tmp $(AM_V_at)cp $(MAN_HTML_FILES) @abs_builddir@/$@.tmp $(AM_V_at)cp $(MAN_XML_FILES) @abs_builddir@/$@.tmp $(AM_V_at)cp $(BONUS_FILES) @abs_builddir@/$@.tmp @@ -142,7 +171,7 @@ maintainer-upload-docs: dbus-docs.tar.gz dbus-docs else maintainer-upload-docs: @echo "Can't upload documentation! Re-run configure with" - @echo " --enable-doxygen-docs --enable-xml-docs" + @echo " --enable-doxygen-docs --enable-xml-docs --enable-ducktype-docs" @echo "and ensure that man2html is installed." @false endif @@ -151,6 +180,8 @@ CLEANFILES = \ $(man1_MANS) \ $(MAN_XML_FILES) \ $(XMLTO_HTML) \ + $(YELP_HTML) \ + $(YELP_STATIC_HTML) \ $(NULL) clean-local: diff --git a/doc/dbus-api-design.duck b/doc/dbus-api-design.duck new file mode 100644 index 0000000..477775e --- /dev/null +++ b/doc/dbus-api-design.duck @@ -0,0 +1,788 @@ += D-Bus API Design Guidelines +@link[guide >index] +@credit[type="author copyright"] + @name Philip Withnall + @email philip.withnall@collabora.co.uk + @years 2015 +@desc Guidelines for writing high quality D-Bus APIs +@revision[date=2015-02-05 status=draft] + +[synopsis] + [title] + Summary + + The most common use for D-Bus is in implementing a service which will be + consumed by multiple client programs, and hence all interfaces exported on the + bus form a public API. Designing a D-Bus API is like designing any other API: + there is a lot of flexibility, but there are design patterns to follow and + anti-patterns to avoid. + + This guide aims to explain the best practices for writing D-Bus APIs. These + have been refined over several years of use of D-Bus in many projects. + Pointers will be given for implementing APIs using common D-Bus + libraries like + $link[>>https://developer.gnome.org/gio/stable/gdbus-convenience.html](GDBus), + but detailed implementation instructions are left to the libraries’ + documentation. Note that you should $em(not) use libdbus-1 or libdbus-glib to + implement D-Bus services, as they are awkward to use correctly, deprecated + and unmaintained. + + For documentation on D-Bus itself, see the + $link[>>http://dbus.freedesktop.org/doc/dbus-specification.html](D-Bus + specification). + +[links section] + +== APIs + [id="apis"] + +A D-Bus API is a specification of one or more interfaces, which will be +implemented by objects exposed by a service on the bus. Typically an API is +designed as a set of $link[>#interface-files](interface files), and the +implementation of the service follows those files. Some projects, however, +choose to define the API in the code for the service, and to export XML +interface files from the running service +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-introspectable](using +D-Bus introspection). Both are valid approaches. + +For simplicity, this document uses the XML descriptions of D-Bus interfaces as +the canonical representation. + +== Interface files + [id="interface-files"] + +A D-Bus interface file is an XML file which describes one or more D-Bus +interfaces, and is the best way of describing a D-Bus API in a machine +readable way. The format is described in the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format](D-Bus +specification), and is supported by tools such as $cmd(gdbus-codegen). + +Interface files should be installed to $code($var($$datadir)/dbus-1/interfaces) +so that other services can load them. There should be one file installed +per D-Bus interface, named after the interface, containing a single top-level +$code() element with a single $code() beneath it. For example, +interface $code(org.example.MyService1.Manager) would be described by file +$file($var($$datadir)/dbus-1/interfaces/org.example.MyService1.Manager.xml): + +[listing] + [title] + Example D-Bus Interface XML + [desc] + A brief example interface XML document. + [code mime="application/xml"] + + + + + + Name of new contact + + + E-mail address of new contact + + + ID of newly added contact + + + + + Adds a new contact to the address book with their name and + e-mail address. + + + + + + + +If an interface defined by service A needs to be used by client B, client B +should declare a build time dependency on service A, and use the installed copy +of the interface file for any code generation it has to do. It should $em(not) +have a local copy of the interface, as that could then go out of sync with the +canonical copy in service A’s git repository. + +== API versioning + [id="api-versioning"] + +$link[>>http://ometer.com/parallel.html](Just like C APIs), D-Bus interfaces +should be designed to be usable in parallel with API-incompatible versions. This +is achieved by including a version number in each interface name, service name +and object path which is incremented on every backwards-incompatible change. + +Version numbers should be included in all APIs from the first release, as that +means moving to a new version is as simple as incrementing the number, rather +than inserting a number everywhere, which takes more effort. + +New API can be added to a D-Bus interface without incrementing the version +number, as such additions are still backwards-compatible. However, clients +should gracefully handle the $code(org.freedesktop.DBus.Error.UnknownMethod) +error reply from all D-Bus method calls so that they can run against older +versions of the service which don’t implement new methods. + +When API is broken, changed or removed, the service’s version number must be +bumped; for example, from $code(org.example.MyService1) +to $code(org.example.MyService2). If backwards compatibility is maintained in +the service by implementing both the old and new interfaces, the service can +own $em(both) well-known names and clients can use whichever they support. + +As discussed in $link[>#annotations], new or deprecated APIs should be marked in +the interface XML using annotations. + +Note, however, that supporting multiple interface versions simultaneously +requires that $em(object paths) are versioned as well, so objects $em(must not) +be put on the bus at the root path (‘/’). This is for technical reasons: signals +sent from a D-Bus service have the originating service name overwritten by its +unique name (e.g. $code(org.example.MyService2) is overwritten by $code(:1:15)). +If object paths are shared between objects implementing two versions of the +service’s interface, client programs cannot tell which object a signal has come +from. The solution is to include the version number in all object paths, for +example $code(/org/example/MyService1/Manager) instead of $code(/) or +$code(/org/example/MyService/Manager). + +In summary, version numbers should be included in all service names, interface +names and object paths: +[list] +* $code(org.example.MyService1) +* $code(org.example.MyService1.InterestingInterface) +* $code(org.example.MyService1.OtherInterface) +* $code(/org/example/MyService1/Manager) +* $code(/org/example/MyService1/OtherObject) + +== API design + [id="api-design"] + +D-Bus API design is broadly the same as C API design, but there are a few +additional points to bear in mind which arise both from D-Bus’ features +(explicit errors, signals and properties), and from its implementation as an IPC +system. + +D-Bus method calls are much more expensive than C function calls, typically +taking on the order of milliseconds to complete a round trip. Therefore, the +design should minimize the number of method calls needed to perform an +operation. + +The type system is very expressive, especially compared to C’s, and APIs should +take full advantage of it. + +Similarly, its support for signals and properties differentiates it from normal +C APIs, and well written D-Bus APIs make full use of these features where +appropriate. They can be coupled with standard interfaces defined in the D-Bus +specification to allow for consistent access to properties and objects in a +hierarchy. + +=== Minimizing Round Trips + [id="round-trips"] + +Each D-Bus method call is a round trip from a client program to a service and +back again, which is expensive — taking on the order of a millisecond. One of +the top priorities in D-Bus API design is to minimize the number of round trips +needed by clients. This can be achieved by a combination of providing specific +convenience APIs and designing APIs which operate on large data sets in a single +call, rather than requiring as many calls as elements in the data set. + +Consider an address book API, $code(org.example.MyService1.AddressBook). It +might have an $code(AddContact(ss) → (u)) method to add a contact (taking name +and e-mail address parameters and returning a unique contact ID), and a +$code(RemoveContact(u)) method to remove one by ID. In the normal case of +operating on single contacts in the address book, these calls are optimal. +However, if the user wants to import a list of contacts, or delete multiple +contacts simultaneously, one call has to be made per contact — this could take +a long time for large contact lists. + +Instead of the $code(AddContact) and $code(RemoveContact) methods, the interface +could have an $code(UpdateContacts(a(ss)au) → (au)) method, which takes an array +of structs containing the new contacts’ details, and an array of IDs of the +contacts to delete, and returns an array of IDs for the new contacts. This +reduces the number of round trips needed to one. + +Adding convenience APIs to replace a series of common method calls with a single +method call specifically for that task is best done after the API has been +profiled and bottlenecks identified, otherwise it could lead to premature +optimization. One area where convenience methods can typically be added +is during initialization of a client, where multiple method calls are needed to +establish its state with the service. + +=== Taking Advantage of the Type System + [id="type-system"] + +D-Bus’ type system is similar to Python’s, but with a terser syntax which may be +unfamiliar to C developers. The key to using the type system effectively is +to expose as much structure in types as possible. In particular, sending +structured strings over D-Bus should be avoided, as they need to be built and +parsed; both are complex operations which are prone to bugs. + +A common, useful approach is to use enumerated values transmitted as unsigned +integers over the bus, with the interface documentation describing what each +value means, and how unrecognized values should be handled. This allows for +extra values to be added in future without breaking API. Integers are better +than strings in this case as they do not require parsing or string matching when +received, and are more compact. + +[example] + For example, instead of: + [code style="invalid" mime="application/xml"] + + + + Use: + [code style="valid" mime="application/xml"] + + + +Similarly, enumerated values should be used instead of booleans, as they allow +extra values to be added in future, and there is no ambiguity about the sense of +the boolean value. + +[example] + For example, instead of: + [code style="invalid" mime="application/xml"] + + + + + + Be more explicit than a boolean: + [code style="valid" mime="application/xml"] + + + + + +Enumerated values should also be used instead of $em(human readable) strings, +which should not be sent over the bus if possible. The service and client could +be running in different locales, and hence interpret any human readable strings +differently, or present them to the user in the wrong language. Transmit an +enumerated value and convert it to a human readable string in the client. + +[example] + For example, instead of: + [code style="invalid" mime="application/xml"] + + + + + + The progress should be reported as an enumerated value: + [code style="valid" mime="application/xml"] + + + + + +D-Bus has none of the problems of signed versus unsigned integers which C has +(specifically, it does not do implicit sign conversion), so integer types should +always be chosen to be an appropriate size and signedness for the data they +could possibly contain. Typically, unsigned values are more frequently needed +than signed values. + +Structures can be used almost anywhere in a D-Bus type, and arrays of structures +are particularly useful. Structures should be used wherever data fields are +related. + +[example] + For example, instead of several identically-indexed arrays containing + different properties of the same set of items: + [code style="invalid" mime="application/xml"] + + + + + + + + The arrays can be combined into a single array of structures: + [code style="invalid" mime="application/xml"] + + + + + + +Note that D-Bus arrays are automatically transmitted with their length, so there +is no need to null-terminate them or encode their length separately. + +[comment] + FIXME: Mention maybe types and the extended kdbus/GVariant type system once + that’s stable and round-trip-ability is no longer a concern. + +=== Using Signals, Properties and Errors + [id="using-the-features"] + +D-Bus method calls are explicitly asynchronous due to the latency inherent in +IPC. This means that peers should not block on receiving a reply from a method +call; they should schedule other work (in a main loop) and handle the reply when +it is received. Even though method replies may take a while, the caller is +$em(guaranteed) to receive exactly one reply eventually. This reply could be the +return value from the method, an error from the method, or an error from the +D-Bus daemon indicating the service failed in some way (e.g. due to crashing). + +Because of this, service implementations should be careful to always reply +exactly once to each method call. Replying at the end of a long-running +operation is correct — the client will patiently wait until the operation has +finished and the reply is received. + +An anti-pattern to avoid in this situation is to start a long-running operation +when a method call is received, then to never reply to the method call and +instead notify the client of completion of the operation via a signal. This +approach is incorrect as signal emissions do not have a one-to-one relationship +with method calls — the signal could theoretically be emitted multiple times, or +never, which the client would not be able to handle. + +Similarly, use a D-Bus error reply to signify failure of an operation triggered +by a method call, rather than using a custom error code in the method’s +reply. This means that a reply always indicates success, and an error always +indicates failure — rather than a reply indicating either depending on its +parameters, and having to return dummy values in the other parameters. Using +D-Bus error replies also means such failures can be highlighted in debugging +tools, simplifying debugging. + +=== Using Standard Interfaces + [id="standard-interfaces"] + +Use standard D-Bus interfaces where possible. + +==== Properties + [id="interface-properties"] + +The D-Bus specification defines the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-properties]($code(org.freedesktop.DBus.Properties)) +interface, which should be used by all objects to notify clients of changes +to their property values, with the $code(PropertiesChanged) signal. This signal +eliminates the need for individual $code($var(PropertyName)Changed) signals, and +allows multiple properties to be notified in a single signal emission, reducing +IPC round trips. Similarly, the $code(Get) and $code(Set) methods can be used to +manipulate properties on an object, eliminating redundant +$code(Get$var(PropertyName)) and $code(Set$var(PropertyName)) methods. + +[example] + For example, consider an object implementing an interface + $code(org.example.MyService1.SomeInterface) with methods: + [list] + * $code(GetName() → (s)) + * $code(SetName(s) → ()) + * $code(GetStatus() → (u)) + * $code(RunOperation(ss) → (u)) + and signals: + [list] + * $code(NameChanged(u)) + * $code(StatusChanged(u)) + + The interface could be cut down to a single method: + [list] + * $code(RunOperation(ss) → (u)) + The object could then implement the $code(org.freedesktop.DBus.Properties) + interface and define properties: + [list] + * $code(Name) of type $code(s), read–write + * $code(Status) of type $code(u), read-only + + The $code(NameChanged) and $code(StatusChanged) signals would be replaced by + $code(org.freedesktop.DBus.Properties.PropertiesChanged). + +==== Object Manager + [id="interface-object-manager"] + +The specification also defines the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager]($code(org.freedesktop.DBus.ObjectManager)) +interface, which should be used whenever a service needs to expose a variable +number of objects of the same class in a flat or tree-like structure. For +example, this could be used by an address book service which exposes multiple +address books, each as a separate object. The $code(GetManagedObjects) method +allows the full object tree to be queried, returning all the objects’ properties +too, eliminating the need for further IPC round trips to query the properties. + +[example] + For example, consider an object implementing an interface + $code(org.example.MyService1.AddressBookManager) with methods: + [list] + * $code(GetAddressBooks() → (ao)) + and signals: + [list] + * $code(AddressBookAdded(o)) + * $code(AddressBookRemoved(o)) + + If the manager object is at path + $code(/org/example/MyService1/AddressBookManager), each address book is a + child object, e.g. + $code(/org/example/MyService1/AddressBookManager/SomeAddressBook). + + The interface could be eliminated, and the + $code(org.freedesktop.DBus.ObjectManager) interface implemented on the manager + object instead. + + Calls to $code(GetAddressBooks) would become calls to $code(GetManagedObjects) + and emissions of the $code(AddressBookAdded) and $code(AddressBookRemoved) + signals would become emissions of $code(InterfacesAdded) and + $code(InterfacesRemoved). + +=== Naming Conventions + [id="naming-conventions"] + +All D-Bus names, from service names through to method parameters, follow a set +of conventions. Following these conventions makes APIs more natural to use and +consistent with all other services on the system. + +Almost all names use camel case with no underscores, as in the examples below. +Method and signal parameters are an exception, using +$code(lowercase_with_underscores). Type information is never encoded in the +parameter name (i.e. $em(not) +$link[>>http://en.wikipedia.org/wiki/Hungarian_notation](Hungarian notation)). + +[example] + [terms] + - Service Name + * $code(org.example.MyService1) + - Interface Name + * $code(org.example.MyService1.SomeInterface) + - Object Path (Root Object) + * $code(/org/example/MyService1) + - Object Path (Child Object) + * $code(/org/example/MyService1/SomeChild) + - Object Path (Grandchild Object) + * $code(/org/example/MyService1/AnotherChild/Grandchild1) + - Method Name + * $code(org.example.MyService1.SomeInterface.MethodName) + - Signal Name + * $code(org.example.MyService1.SomeInterface.SignalName) + - Property Name + * $code(org.example.MyService1.SomeInterface.PropertyName) + +See also: $link[>#api-versioning]. + +== Code generation + [id="code-generation"] + +Rather than manually implementing both the server and client sides of a D-Bus +interface, it is often easier to write the interface XML description and use a +tool such as +$link[>>https://developer.gnome.org/gio/stable/gdbus-codegen.html]($cmd(gdbus-codegen)) +to generate type-safe C APIs, then build the implementation using those. This +avoids the tedious and error-prone process of writing code to build and read +D-Bus parameter variants for each method call. + +Use of code generators is beyond the scope of this guide; for more information, +see the +$link[>>https://developer.gnome.org/gio/stable/gdbus-codegen.html]($cmd(gdbus-codegen) +manual). + +== Annotations + [id="annotations"] + +Annotations may be added to the interface XML to expose metadata on the API +which can be used by documentation or code generation tools to modify their +output. Some standard annotations are given in the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format](D-Bus +specification), but further annotations may be defined by specific tools. + +For example, $cmd(gdbus-codegen) defines several useful annotations, listed on +its man page. + +The following annotations are the most useful: + +[terms] +- $code(org.freedesktop.DBus.Deprecated) +* Mark a symbol as deprecated. This should be used whenever the API is changed, + specifying the version introducing the deprecation, the reason for + deprecation, and a replacement symbol to use. +- $code(org.gtk.GDBus.Since) +* Mark a symbol as having been added to the API after the initial release. This + should include the version the symbol was first added in. +- $code(org.gtk.GDBus.C.Name) and $code(org.freedesktop.DBus.GLib.CSymbol) +* Both used interchangeably to hint at a C function name to use when generating + code for a symbol. Use of this annotation can make generated bindings a lot + more pleasant to use. +- $code(org.freedesktop.DBus.Property.EmitsChangedSignal) +* Indicate whether a property is expected to emit change signals. This can + affect code generation, but is also useful documentation, as client programs + then know when to expect property change notifications and when they have to + requery. + +== Documentation + [id="documentation"] + +Also just like C APIs, D-Bus APIs must be documented. There are several methods +for documenting the interfaces, methods, properties and signals in a D-Bus +interface XML file, each unfortunately with their own downsides. You should +choose the method which best matches the tooling and workflow you are using. + +=== XML Comments + +XML comments containing documentation in the +$link[>>https://developer.gnome.org/gtk-doc-manual/stable/documenting_syntax.html.en](gtk-doc +format) is the recommended format for use with +$link[>>https://developer.gnome.org/gio/stable/gdbus-codegen.html]($cmd(gdbus-codegen)). +Using $cmd(gdbus-codegen), these comments can be extracted, converted to DocBook +format and included in the project’s API manual. For example: + +[listing] + [title] + Documentation Comments in D-Bus Interface XML + [desc] + Example gtk-doc–style documentation comments in the introspection XML for + the $code(org.freedesktop.DBus.Properties) interface. + [code mime="application/xml"] + + + + + + + + + + + + + + + + + +=== XML Annotations + +Documentation can also be added as annotation elements in the XML. This format +is also supported by $cmd(gdbus-codegen), but gtk-doc comments are preferred. +For example: + +[listing] + [title] + Documentation Annotations in D-Bus Interface XML + [desc] + Example GDBus documentation annotations in the introspection XML for + the $code(org.freedesktop.DBus.Properties) interface. + [code mime="application/xml"] + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +== Service files + [id="service-files"] + +Each D-Bus service must install a $file(.service) file describing its service +name and the command to run to start the service. This allows for service +activation (see the +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-starting-services](D-Bus +specification)). + +Service files must be named after the service’s well-known name, for example +file $file(org.example.MyService1.service) for well-known name +$code(org.example.MyService1). Files must be installed in +$file($$(datadir)/dbus-1/services) for the session bus and +$file($$(datadir)/dbus-1/system-services) for the system bus. Note, however, +that services on the system bus almost always need a +$link[>#security-policies](security policy) as well. + +== Security Policies + [id="security-policies"] + +Full coverage of securing D-Bus services is beyond the scope of this guide, +however there are some steps which you can take when designing an API to ease +security policy implementation. + +D-Bus security policies use an allow/deny model, where each message (method +call, signal emission, etc.) can be allowed or denied according to the sum of +all policy rules which match it. When designing an API, bear in mind the need to +write such a security policy, and consider splitting up methods or providing +more restricted versions which accept constrained parameters, so that they +can be exposed with less restrictive security policies if needed by less trusted +clients. + +Secondly, the default D-Bus security policy is restrictive enough to allow +sensitive data, such as passwords, to be safely sent over the bus in unicast +messages (including unicast signals); so there is no need to complicate APIs by +implementing extra security. Note, however, that sensitive data must $em(not) be +sent in broadcast signals, as they can be seen by all peers on the bus. + +== Debugging + [id="debugging"] + +Debugging services running on D-Bus can be tricky, especially if they are +launched via service activation and hence in an environment out of your control. + +Three main tools are available: D-Bus Monitor, Bustle and D-Feet. + +=== D-Bus Monitor + [id="dbus-monitor"] + +$link[>>http://dbus.freedesktop.org/doc/dbus-monitor.1.html]($cmd(dbus-monitor)) +is a core D-Bus tool, and allows eavesdropping on the session or system bus, +printing all messages it sees. The messages may be filtered using a standard +$link[>>http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-routing-match-rules](D-Bus +match rule) to make the stream more manageable. + +Previous versions of D-Bus have required the bus’ security policy to be manually +relaxed to allow eavesdropping on all messages, especially on the system bus. +This meant that debugging the system bus was difficult and insecure. The latest +versions of D-Bus add support for monitor-only connections for the root user, +which means that $cmd(dbus-monitor) can be run as root to painlessly monitor all +messages on the system bus without modifying its security policy. + +=== Bustle + [id="bustle"] + +$link[>>http://willthompson.co.uk/bustle/](Bustle) is a graphical version of +$cmd(dbus-monitor), with a UI focused on profiling D-Bus performance by plotting +messages on a timeline. It is ideal for finding bottlenecks in IPC performance +between a service and client. + +=== D-Feet + [id="d-feet"] + +$link[>>https://wiki.gnome.org/Apps/DFeet](D-Feet) is an introspection tool for +D-Bus, which displays all peers on the bus graphically, with their objects, +interfaces, methods, signals and properties listed for examination. It is useful +for debugging all kinds of issues related to presence of services on the bus +and the objects they expose. -- 1.9.3