{"id":46,"date":"2006-06-19T19:20:35","date_gmt":"2006-06-19T17:20:35","guid":{"rendered":"https:\/\/raphael.slinckx.net\/blog\/test-page\/dbus-tutorial\/"},"modified":"2006-06-19T20:52:13","modified_gmt":"2006-06-19T18:52:13","slug":"dbus-tutorial","status":"publish","type":"page","link":"https:\/\/raphael.slinckx.net\/blog\/documents\/dbus-tutorial","title":{"rendered":"DBus Activation Tutorial"},"content":{"rendered":"<h3>The DBus missing tutorial &#8211; DBus Activation<\/h3>\n<p>I&#8217;ll explain the activation part of dbus, that&#8217;s how you can request the dbus daemon to automatically<br \/>\nstart a program that provide a given service. You will also find general dbus tips in there.<\/p>\n<h4>Intro<\/h4>\n<p>We have a program in <code>\/usr\/bin\/program-providing-servicename<\/code> providing service<br \/>\n<code>org.gnome.ServiceName<\/code>, and we want that program to be started whenever dbus needs<br \/>\nthat service (if it isn&#8217;t already running). On the other end, we have a client that<br \/>\nwants to use the service, and doesn&#8217;t care wether it&#8217;s running or not, it just want to access the service.<\/p>\n<p>See the <a href=\"#credits\">end of the tutorial<\/a> for credits, external links, changelog, and license.<\/p>\n<h4>Autotools<\/h4>\n<p>Here is the layout we will use for our example program:<\/p>\n<pre>\r\nprogram-\r\n       |-data-\r\n       |     |-Makefile.am\r\n       |     |-datafiles.[png,desktop,..]\r\n       |     |-org.gnome.ServiceName.service.in\r\n       |\r\n       |-src-\r\n       |     |-Makefile.am\r\n       |     |-server.[hc]\r\n       |     |-client.[hc]\r\n       |     |-server-bindings.h\r\n       |     |-client-bindings.h\r\n       |     |-servicename-infos.xml\r\n       |\r\n       |-configure.ac\r\n       |-acinclude.m4\r\n<\/pre>\n<p>Let&#8217;s see what goes in each file. The main purpose of all this is to install a .service file, which is used<br \/>\nby the dbus-daemon to launch a process owning a given service.<\/p>\n<h5>program\/configure.ac<\/h5>\n<p>We need to retrieve the directory on the system in which to place the .service file. With modern DBuses (>0.30)<br \/>\nit is placed in <code>\/usr\/share\/dbus-1\/services\/<\/code>. The following m4 macro to expand the $datadir variable is useful. Put this<br \/>\nin your configure.ac file:<\/p>\n<pre>\r\nAS_AC_EXPAND(DATADIR, $datadir)\r\n\r\nDBUS_SERVICES_DIR=\"$DATADIR\/dbus-1\/services\"\r\nAC_SUBST(DBUS_SERVICES_DIR)\r\nAC_DEFINE_UNQUOTED(DBUS_SERVICES_DIR, \"$DBUS_SERVICES_DIR\", [Where services dir for DBUS is])\r\n<\/pre>\n<p>Then you need to place this code snippet in <code>program\/acinclude.m4<\/code> file, or append it to the existing one:<\/p>\n<pre>\r\ndnl AS_AC_EXPAND(VAR, CONFIGURE_VAR)\r\ndnl\r\ndnl example\r\ndnl AS_AC_EXPAND(SYSCONFDIR, $sysconfdir)\r\ndnl will set SYSCONFDIR to \/usr\/local\/etc if prefix=\/usr\/local\r\n\r\nAC_DEFUN([AS_AC_EXPAND],\r\n[\r\n  EXP_VAR=[$1]\r\n  FROM_VAR=[$2]\r\n\r\n  dnl first expand prefix and exec_prefix if necessary\r\n  prefix_save=$prefix\r\n  exec_prefix_save=$exec_prefix\r\n\r\n  dnl if no prefix given, then use \/usr\/local, the default prefix\r\n  if test \"x$prefix\" = \"xNONE\"; then\r\n    prefix=$ac_default_prefix\r\n  fi\r\n  dnl if no exec_prefix given, then use prefix\r\n  if test \"x$exec_prefix\" = \"xNONE\"; then\r\n    exec_prefix=$prefix\r\n  fi\r\n\r\n  full_var=\"$FROM_VAR\"\r\n  dnl loop until it doesn't change anymore\r\n  while true; do\r\n    new_full_var=\"`eval echo $full_var`\"\r\n    if test \"x$new_full_var\"=\"x$full_var\"; then break; fi\r\n    full_var=$new_full_var\r\n  done\r\n\r\n  dnl clean up\r\n  full_var=$new_full_var\r\n  AC_SUBST([$1], \"$full_var\")\r\n\r\n  dnl restore prefix and exec_prefix\r\n  prefix=$prefix_save\r\n  exec_prefix=$exec_prefix_save\r\n])\r\n<\/pre>\n<h5>data\/Makefile.am<\/h5>\n<p>DBus needs a .service file to know what program to launch for a given service name.<br \/>\nThat file has to be in <code>\/usr\/share\/dbus-1\/services\/<\/code>, add this snippet to your automake file:<\/p>\n<pre>\r\n# Dbus service file\r\nservicedir = $(DBUS_SERVICES_DIR)\r\nservice_in_files = org.gnome.ServiceName.service.in\r\nservice_DATA = $(service_in_files:.service.in=.service)\r\n\r\n# Rule to make the service file with bindir expanded\r\n$(service_DATA): $(service_in_files) Makefile\r\n\t@sed -e \"s|@bindir@|$(bindir)|\" $<> $@\r\n<\/pre>\n<p>The content of that <code>data\/org.gnome.ServiceName.service.in<\/code> file is the following:<\/p>\n<pre>\r\n[D-BUS Service]\r\nName=org.gnome.ServiceName\r\nExec=@bindir@\/program-providing-servicename\r\n<\/pre>\n<h5>src\/Makefile.am<\/h5>\n<p>In this file we generate glib bindings headers that define method stubs to be implemented\/called by the server\/client. DBus comes with<br \/>\na tool called <code>dbus-binding-tool<\/code> that can generate both client and server header files. Let&#8217;s see what we<br \/>\nwrite in the Makefile.am in addition to the usual stuff:<\/p>\n<pre>\r\nBUILT_SOURCES = server-bindings.h client-bindings.h\r\n# We don't want to install this header\r\nnoinst_HEADERS = $(BUILT_SOURCES)\r\n\r\n# Correctly clean the generated headers, but keep the xml description\r\nCLEANFILES = $(BUILT_SOURCES)\r\nEXTRA_DIST = servicename-infos.xml\r\n\r\n#Rule to generate the binding headers\r\nserver-bindings.h:  servicename-infos.xml\r\n\tdbus-binding-tool --prefix=server_object --mode=glib-server $<> $@\r\n\r\nclient-bindings.h:  servicename-infos.xml\r\n\tdbus-binding-tool --prefix=server_object --mode=glib-client $<> $@\r\n<\/pre>\n<h4>Code to use that stuff<\/h4>\n<p>Now you have your autotools setup to correctly prepare and install the service file, Dbus knows how to launch<br \/>\nthe program corresponding to your service. If the program is already running, nothing happens. If the program isn&#8217;t running,<br \/>\ndbus launches the program, waits for it to take ownership of the service, then executes the remote method and returns the result.<br \/>\nThe following section will most likely be similar in some parts to the<br \/>\n<a href=\"http:\/\/dbus.freedesktop.org\/doc\/dbus-tutorial.html\">dbus tutorial<\/a><\/p>\n<h5>The XML description<\/h5>\n<p>The <code>src\/servicename-infos.xml<\/code> is a description of the interface provided by service, here is a quick example, we<br \/>\ndefine one method that takes a string as parameter and returns a string.<\/p>\n<pre>\r\n&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\r\n\r\n&lt;node name=\"\/org\/gnome\/ServiceName\"&gt;\r\n\t&lt;interface name=\"org.gnome.ServiceName\"&gt;\r\n\t\t&lt;annotation name=\"org.freedesktop.DBus.GLib.CSymbol\" value=\"server\"\/&gt;\r\n\t\t&lt;method name=\"EchoString\"&gt;\r\n\t\t\t&lt;arg type=\"s\" name=\"original\" direction=\"in\" \/&gt;\r\n\t\t\t&lt;arg type=\"s\" name=\"echo\" direction=\"out\" \/&gt;\r\n\t\t&lt;\/method&gt;\r\n\t\t&lt;!-- Add more methods\/signals if you want --&gt;\r\n\t&lt;\/interface&gt;\r\n&lt;\/node&gt;\r\n<\/pre>\n<ul>\n<li><code>direction=\"in\"<\/code> means a parameter to the method, <code>direction=\"out\"<\/code> is a return value<\/li>\n<li><code>type=\"s\"<\/code> means a string, see <a href=\"http:\/\/dbus.freedesktop.org\/doc\/dbus-tutorial.html\">dbus tutorial<\/a> for more types signatures<\/li>\n<li><code>name=\"xxx\"<\/code> is purely decorative<\/li>\n<li>The annotation is the prefix used in the server binding, in this case, all our server method stubs will be prefixed by server_,<br \/>\n that means we will have a method called <code>server_echo_string<\/code> to implement.<\/li>\n<\/ul>\n<p>The server needs to <code>#include \"server-bindings.h\"<\/code> header,<br \/>\nthe client needs to <code>#include \"client-bindings.h\"<\/code> header. When we look at that generate header we can see how the XML<br \/>\ndescription has been translated to C method calls.<\/p>\n<h6>Server implementation<\/h6>\n<p>In the server, we must ensure that we will take ownership of the provided service, otherwise the activation procedure won&#8217;t succeed.<\/p>\n<p>In the <code>class_init<\/code> of your GObject, you must install the dbus introspection infos, and in the <code>init<\/code> function we will<br \/>\nregister for the service.<\/p>\n<pre>\r\n#include &lt;dbus\/dbus-glib-bindings.h&gt;\r\n\r\n\/* Standard GObject class structures, etc *\/\r\ntypedef struct\r\n{\r\n\tDBusGConnection *connection;\r\n}ServerObjectClass;\r\n\r\nclass_init(ServerObjectClass *klass)\r\n{\r\n\tGError *error = NULL;\r\n\r\n\t\/* Init the DBus connection, per-klass *\/\r\n\tklass->connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);\r\n\tif (klass->connection == NULL)\r\n\t{\r\n\t\tg_warning(\"Unable to connect to dbus: %s\", error->message);\r\n\t\tg_error_free (error);\r\n\t\treturn;\r\n\t}\r\n\r\n\t\/* &dbus_glib__object_info is provided in the server-bindings.h file *\/\r\n\t\/* OBJECT_TYPE_SERVER is the GType of your server object *\/\r\n\tdbus_g_object_type_install_info (OBJECT_TYPE_SERVER, &dbus_glib__object_info);\r\n}\r\n\r\ninit(ServerObject *server)\r\n{\r\n\tGError *error = NULL;\r\n\tDBusGProxy *driver_proxy;\r\n\tServerObjectClass *klass = SERVER_OBJET_GET_CLASS (server);\r\n\tint request_ret;\r\n\r\n\t\/* Register DBUS path *\/\r\n\tdbus_g_connection_register_g_object (klass->connection,\r\n\t\t\t\"\/org\/gnome\/ServiceName\",\r\n\t\t\tG_OBJECT (server));\r\n\r\n\t\/* Register the service name, the constant here are defined in dbus-glib-bindings.h *\/\r\n\tdriver_proxy = dbus_g_proxy_new_for_name (klass->connection,\r\n\t\t\tDBUS_SERVICE_DBUS,\r\n\t\t\tDBUS_PATH_DBUS,\r\n\t\t\tDBUS_INTERFACE_DBUS);\r\n\r\n\tif(!org_freedesktop_DBus_request_name (driver_proxy,\r\n\t\t\t\"org.gnome.ServiceName\",\r\n\t\t\t0, &request_ret,    \/* See tutorial for more infos about these *\/\r\n\t\t\t&error))\r\n\t{\r\n\t\tg_warning(\"Unable to register service: %s\", error->message);\r\n\t\tg_error_free (error);\r\n\t}\r\n\tg_object_unref (driver_proxy);\r\n}\r\n<\/pre>\n<p>Now our server object is ready, and is available on dbus to answer remote calls. We must now provide an implementation<br \/>\nof the exported methods.<\/p>\n<p>The server-bindings.h shows a marshaller declaration of type<br \/>\n<code>dbus_glib_marshal__BOOLEAN__STRING_POINTER_POINTER<\/code>. We must implement a function called <code>server_echo_string<\/code><br \/>\nthat respect this prototype. Note that the annotation is translated with a <code>server_<\/code> prefix to the method, and the method name<br \/>\nis mangled to a C style name <code>xx_xxx<\/code>. That method must return TRUE in case of success, or FALSE if an error occured, and in<br \/>\nthat case, the GError must be set. The return values are given as pointers to the return location.<\/p>\n<pre>\r\ngboolean\r\nserver_echo_string (ServerObject *server, gchar *original, gchar **echo, GError **error)\r\n{\r\n\t*echo = g_strdup (original);\r\n\r\n\tif (problem)\r\n\t{\r\n\t\t\/* We have an error, set the gerror *\/\r\n\t\tg_set_error (error, g_quark_from_static_string (\"echo\"),\r\n\t\t\t\t\t0xdeadbeef,\r\n\t\t\t\t\t\"Some random problem occured, you're screwed\");\r\n\t\treturn FALSE;\r\n\t}\r\n\r\n\treturn TRUE;\r\n}\r\n<\/pre>\n<h6>Client implementation<\/h6>\n<p>The <code>client-bindings.h<\/code> shows the prototypes of the function we can call to execute a remote request transparently.<br \/>\nBefore the call a proxy object must be created, this is also shown. In real situations, the two pieces of code<br \/>\nwould be placed in their own place, for example the creation of the proxy in the object contructor, and the method call in a callback<br \/>\nfor a button, this is just an exemple to show the base mecanism.<\/p>\n<pre>\r\n\/* Somewhere in the code, we want to execute EchoString remote method *\/\r\nDBusGProxy *proxy;\r\nDBusGConnection *connection;\r\nGError *error = NULL;\r\ngchar *result;\r\n\r\nconnection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);\r\nif (connection == NULL)\r\n{\r\n\tg_warning(\"Unable to connect to dbus: %sn\", error->message);\r\n\tg_error_free (error);\r\n\t\/* Basically here, there is a problem, since there is no dbus :) *\/\r\n\treturn;\r\n}\r\n\r\n\/* This won't trigger activation! *\/\r\nproxy = dbus_g_proxy_new_for_name (connection,\r\n\t\t\"org.gnome.ServiceName\",\r\n\t\t\"\/org\/gnome\/ServiceName\",\r\n\t\t\"org.gnome.ServiceName\");\r\n\r\n\/* The method call will trigger activation, more on that later *\/\r\nif (!org_gnome_ServiceName_echo_string (proxy, \"The string we want echo-ed\", &result, &error))\r\n{\r\n\t\/* Method failed, the GError is set, let's warn everyone *\/\r\n\tg_warning (\"Woops remote method failed: %s\", error->message);\r\n\tg_error_free (error);\r\n\treturn;\r\n}\r\n\r\ng_print (\"We got the folowing result: %s\", result);\r\n\r\n\/* Cleanup *\/\r\ng_free (result);\r\ng_object_unref (proxy);\r\n\r\n\/* The DBusGConnection should never be unreffed, it lives once and is shared amongst the process *\/\r\n<\/pre>\n<p>This first snippet shows a simple method call. First we get the dbus connection, then we create a proxy object that<br \/>\nwill talk with dbus, and finally we call the remote method using the binding header. You see that the prototype is the<br \/>\nsame as the one we implemented in the server, boolean return value indicating succes or failure,<br \/>\npointers to method result values are passed after the parameters. In case of error (return FALSE) , the GError is set.<\/p>\n<p>Now the activation bit, when you create the proxy nothing happens yet, but when you call the remote method, dbus will check to see<br \/>\nif the service exists on the bus.<\/p>\n<ul>\n<li>If the service already exists, the method is called, blocks until the result is returned.<\/li>\n<li>If the service does not exist, dbus searches the <code>dbus-1\/services\/*.service<\/code> files until it finds a<br \/>\nmatching service descriptor.<\/p>\n<ul>\n<li>When the service file is found, the method blocks, dbus launches the associated executable, wait for the executable to take<br \/>\nownership of the service, execute the remote call, and finally returns.<\/li>\n<li>When the service file isn&#8217;t found, it returns an error.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>The above procedure means that your program will block during the process launch, which can be bad, for example in case of a gtk mainloop.<br \/>\nFortunately, there is also a way to do asynchronous calls, and very easily!<\/p>\n<h6>Client asynchronous implementation<\/h6>\n<p>Everything can be copied from the previous example, excepted the actual remote method call, this one now becomes:<\/p>\n<pre>\r\n[... Get the dbus connection, create the proxy ...]\r\n\r\n\/* We now call the method asynchronously *\/\r\norg_gnome_ServiceName_echo_string_async (proxy,\r\n\t\t\"The string we want echo-ed\",\r\n\t\tclient_echo_reply,    \/* See below *\/\r\n\t\tclient);\r\n\r\n\/* Of course, do not unref the proxy now, since we need it for the callback *\/\r\n<\/pre>\n<p>As you can see, we give the parameters directly, but instead of giving the error, and return pointers, we pass<br \/>\na callback <code>client_echo_reply<\/code> that will be called when the method call has finished. We can also pass<br \/>\nany user data as gpointer, here we pass the hypothetical ClientObject instance<\/p>\n<p>Now we must implement the callback<\/p>\n<pre>\r\nstatic void\r\nclient_echo_reply (DBusGProxy *proxy, char *answer, GError *error, gpointer userdata)\r\n{\r\n\tClientObject *client = CLIENT_OBJECT (userdata);\r\n\tif (error!= NULL)\r\n\t{\r\n\t\tg_warning (\"An error occured while calling echo_string remote method: %s\", error->message);\r\n\t\tg_error_free (error);\r\n\t\treturn;\r\n\t}\r\n\r\n\tg_print (\"We got an echo reply, result: %sn\", answer);\r\n}\r\n<\/pre>\n<p>That&#8217;s it, that was a piece of cake! Now as you can guess, the async call will also trigger the activation, if needed,<br \/>\nbut now your program won&#8217;t block until the remote application has started, you can happily continue your mainloop, and be<br \/>\nnotified when the remote program gives the answer!<\/p>\n<p>What happens if the remote program fails to launch? Dbus has a timeout timer, if after a given time, no program<br \/>\nhas claimed the requested service, the method callback is called with the GError set, that&#8217;s it.<\/p>\n<h4>Python Service<\/h4>\n<p>You can also provide the service with a python program, here is an example:<\/p>\n<pre>\r\n#!\/usr\/bin\/env python\r\nimport gtk\r\nimport dbus\r\nimport dbus.service\r\nif getattr(dbus, 'version', (0,0,0)) >= (0,41,0):\r\n\timport dbus.glib\r\n\r\nclass ServerObject(dbus.service.Object):\r\n\tdef __init__(self):\r\n\t\t# Here the service name\r\n\t\tbus_name = dbus.service.BusName('org.gnome.ServiceName',bus=dbus.SessionBus())\r\n\t\t# Here the object path\r\n\t\tdbus.service.Object.__init__(self, bus_name, '\/org\/gnome\/ServiceName')\r\n\r\n\t# Here the interface name, and the method is named same as on dbus.\r\n\t@dbus.service.method('org.gnome.ServiceName')\r\n\tdef EchoString(self, original):\r\n\t\treturn original\r\n\r\nserver = ServerObject()\r\ngtk.main()\r\n<\/pre>\n<p>Just save that as <code>\/usr\/bin\/program-providing-servicename<\/code>, make it executable..<\/p>\n<h4>Random comments<\/h4>\n<ul>\n<li>The executable given in the .service file can be a normal binary file,<br \/>\nbut also a <code>!#\/usr\/bin\/sh<\/code>-type file, like a shell script, or a python script<\/li>\n<li>Never unref DBusGConnection<\/li>\n<li>The server&#8217;s method prototypes must come before you include the server-bindings.h file<\/li>\n<li>ALWAYS use a &#8211;prefix with dbus-bindings-tool, otherwise problems will arise when mixing multiple codes that use dbus independently<\/li>\n<\/ul>\n<p><a name=\"credits\"><\/a><\/p>\n<h4>ChangeLog<\/h4>\n<ul>\n<li>2005-08-20: Update the automake snippet with BUILT_SOURCES, otherwise the header doesn&#8217;t auto-build with make<\/li>\n<\/ul>\n<h4>Credits and links<\/h4>\n<ul>\n<li>The original and official <a href=\"http:\/\/dbus.freedesktop.org\/doc\/dbus-tutorial.html\">dbus tutorial<\/a><\/li>\n<li>Maybe outdated other EchoObjects dbus examples on <a href=\"http:\/\/www.burtonini.com\/blog\/computers\/dbus-2005-04-04-14-14\">Ross Burtonini&#8217;s blog<\/a><\/li>\n<li>Official dbus <a href=\"http:\/\/dbus.freedesktop.org\/doc\/api\/html\/group__DBusGLib.html\">Glib bindings API<\/a><\/li>\n<\/ul>\n<h4>Creative Commons license<\/h4>\n<p><a rel=\"license\" href=\"http:\/\/creativecommons.org\/licenses\/by-nc-sa\/2.0\/be\/\"><br \/>\n\t<img decoding=\"async\" alt=\"Contrat Creative Commons\" src=\"http:\/\/creativecommons.org\/images\/public\/somerights20.gif\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>The DBus missing tutorial &#8211; DBus Activation I&#8217;ll explain the activation part of dbus, that&#8217;s how you can request the dbus daemon to automatically start a program that provide a given service. You will also find general dbus tips in there. Intro We have a program in \/usr\/bin\/program-providing-servicename providing service org.gnome.ServiceName, and we want that [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":47,"menu_order":0,"comment_status":"open","ping_status":"open","template":"page-comments.php","meta":{"footnotes":""},"class_list":["post-46","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/pages\/46","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/comments?post=46"}],"version-history":[{"count":0,"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/pages\/46\/revisions"}],"up":[{"embeddable":true,"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/pages\/47"}],"wp:attachment":[{"href":"https:\/\/raphael.slinckx.net\/blog\/wp-json\/wp\/v2\/media?parent=46"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}