source: project/wiki/eggref/5/dbus @ 40272

Last change on this file since 40272 was 40272, checked in by Shawn Rutledge, 2 months ago

dbus-monitor output; more little fixes

File size: 16.1 KB
Line 
1[[tags: egg]]
2
3== dbus
4
5[[toc:]]
6
7=== Overview
8
9This is a binding for [[https://dbus.freedesktop.org/doc/api/html/index.html|libdbus]] 1.x.  [[https://www.freedesktop.org/wiki/Software/dbus/|D-Bus]] is a popular IPC (inter-process communication) protocol which is often used between system components (for example hald informs applications when new hardware becomes available, gpsd informs apps when the location has changed, or an application requests gsmd to place a phone call).
10
11=== Goals & status
12
13<table>
14<tr><th>Goal</th><th>Achieved?</th></tr>
15<tr><td>send signals</td><td>yes</td></tr>
16<tr><td>call methods in other processes, and get the return values</td><td>yes</td></tr>
17<tr><td>call methods in other processes asynchronously, and the return values come back to a callback later</td><td></td></tr>
18<tr><td>register a procedure as a handler which will be called when a signal is received</td><td>yes</td></tr>
19<tr><td>register a procedure as a method which can be called from another process</td><td>yes</td></tr>
20<tr><td>assign a path to a TinyClos object and map applicable generic functions as D-Bus methods</td><td></td></tr>
21<tr><td>create proxy methods matching remote methods</td><td>yes</td></tr>
22<tr><td>create proxy objects matching remote objects (so that all the remote object's applicable methods are made into local proxy methods)</td><td></td></tr>
23<tr><td>discover services locally</td><td>yes</td></tr>
24<tr><td>discover services on nearby machines</td><td>impossible with dbus-daemon as-is</td></tr>
25<tr><td>discover methods and signals provided by services</td><td>XML only so far</td></tr>
26<tr><td>registered methods and signal-handlers are automatically included in the Introspectable interface implementation</td><td></td></tr>
27<tr><td>user code to do any of the above should be minimal: abstract away the orthogonal extra steps (open a connection, start a polling thread, etc.)</td><td>yes</td></tr>
28<tr><td>support all D-Bus data types for method parameters and return values</td><td></td></tr>
29</table>
30
31=== Author
32
33[[Shawn Rutledge]]
34
35=== Repository
36
37[[https://github.com/ec1oud/dbus-egg|https://github.com/ec1oud/dbus-egg]]
38
39
40=== Requirements
41
42[[http://dbus.freedesktop.org/|libdbus and its headers]]
43
44
45=== License
46
47libdbus has historically had a GPL license.  Later they switched to AFL / GPL dual license.  This egg is released under the MIT license, which is compatible with AFL.
48
49=== Version
50
51As of version 0.96, it seems to be more or less working, but the interface should not be considered "frozen" yet.
52
53=== Terminology
54
55; bus : The aggregator and dispatcher of messages between processes.  The usual choices are system bus, session bus or an app-specific bus.  This egg uses the session bus by default.  It is the same bus used by desktop session services, like KDE components for example.  Accessing the system bus requires special permission.  An app-specific bus does not promote application interoperability.  So that leaves the session bus as generally being the most appropriate.
56
57; bus name : A unique identifier for a D-Bus client.  Every D-Bus client is automatically assigned a bus name.  These consist of a colon character ':' followed by some numbers.  You can see these in the D-Bus traffic in the output of dbus-monitor.  D-Bus clients that want to listen for method calls can request a second bus name, a so-called well-known bus name, which is a name by which other D-Bus clients can refer to them.  Well-known bus names are what you use in the 'service' field of a method call.
58
59; service : A well-known bus name giving the destination of a method call.  Conventionally, these look like reversed domain names.  Signals do not use services.
60
61; path : A D-Bus-exposed API can be conceptually organized into a hierarchy.  Paths index into this hierarchy with notation familiarly derived from file and URL paths, where path components are separated by slashes '/'.  The program might map path components to actual objects in memory, but this is not required.  The default path, when not given, is "/".
62
63; interface : a partitioned set of methods which is being offered, like a Java interface or an abstract class.  An interface is technically optional when registering a method or signal handler, but this egg currently requires it.
64
65; member name : also called method name, or the name of the signal or message.
66
67; signal : a one-to-many notification from one process to any others which happen to be listening for that particular notification.  A signal message does not include a service because it does not have a specific destination.  Signals are one way; there is no explicit reply from the receivers.
68
69; method call : a one-to-one message between D-Bus clients.  The caller can pass arguments, and the handler sends a method return message back with the result.
70
71; method return : the message type of the response to a method call.
72
73; method : a scheme function to be invoked when a particular method call message is received.  Its result will be sent back to the calling client as a method return.
74
75; message : the D-Bus-protocol representation of a signal or a method call passing across the D-Bus connection between two processes.
76
77And this egg introduces one more:
78
79; context : a combination of bus, service, interface and path.  That and a method name will get you a unique identifier for a callback (AKA method or message handler).
80
81This egg does not yet fully support making methods, signal handlers, and interfaces discoverable by other clients.
82
83=== D-Bus in general
84
85Use dbus-monitor to see all the D-Bus traffic (on the session bus by default).
86
87A many-to-many "bus" exists as a running dbus-daemon process.  Each application which is interested in using that bus connects to the daemon via Unix-domain sockets.  The daemon collects incoming messages and dispatches them to the connected processes which have expressed interest in receiving particular categories of messages.  Usually there is one running instance of dbus-daemon for the system bus, and one running instance for each session bus: each user who is logged in and running a desktop session is typically running one instance of the daemon.
88
89A one-to-one application-specific bus can bypass the daemon: one app connects directly to the other.  But this is not a flexible service-oriented approach to IPC, so it is not usually done, and this egg does not support it.
90
91=== Data types
92
93D-Bus protocol (unlike XML, for example) allows sending real native datatypes across the connection.  In fact it provides more data types than does Chicken.  So when you pass some parameters along with a method call, these are the default conversions:
94
95<table>
96<tr><th>Chicken data type</th><th>D-Bus data type</th></tr>
97<tr><td>fixnum</td><td>DBUS_TYPE_INT32</td></tr>
98<tr><td>flonum</td><td>DBUS_TYPE_DOUBLE</td></tr>
99<tr><td>boolean</td><td>DBUS_TYPE_BOOLEAN</td></tr>
100<tr><td>vector</td><td>DBUS_TYPE_ARRAY</td></tr>
101<tr><td>anything else</td><td>DBUS_TYPE_STRING</td></tr>
102</table>
103
104TODO that table needs updating: 64-bit ints are there, signed and unsigned, variants, dictionaries, structs etc.  Also need to explain about unboxing.
105
106When a D-Bus message is received, the parameters are converted similarly, but unsupported D-Bus types will be converted to #f.
107
108Watch out for cases when Chicken converts an integer to a flonum, because it was too large to fit in a fixnum.  It will go across the D-Bus connection as a double, which might not be what you wanted.
109
110=== Exported functions
111
112<procedure>(make-context #!key (bus session-bus) service interface (path "/"))</procedure>
113
114Glom together a bus, service, interface and path into an object that
115you can conveniently hold for later use.  The choices for bus are
116{{session-bus}} (which is the default if you don't specify it),
117{{system-bus}} and {{starter-bus}}.  The service, interface
118and path can be given as strings or symbols.  Symbols would be
119preferred; if you provide strings, {{make-context}} will convert
120them to symbols.  The default for path is "/" so if you don't need a
121hierarchical organization of "objects", you don't need to specify it.
122
123
124<procedure>(send context name . params)</procedure>
125
126Send a signal.
127
128
129<procedure>(call context name . params)</procedure>
130
131Call a remote method, wait for the reply, and receive the return
132values from the remote method, as a list.  Note that since this is a
133blocking call (it waits for the reply), it's not suitable for
134implementing the [[http://en.wikipedia.org/wiki/Actor_model|actor model]]
135or anything similarly lightweight/low-latency.
136
137
138<procedure>(make-method-proxy context name)</procedure>
139
140Wrap {{call}} in a lambda, which you can then use like any local
141function.  The return value from the function thus created will be the
142list of return values from the remote method.
143
144
145<procedure>(register-signal-handler context name msg-cb)</procedure>
146
147Provide a handler to be called when the current process receives a
148D-Bus signal which matches the given context and the given signal name.
149Start a [[/egg/srfi-18|SRFI-18]] thread to poll for incoming signals (unless polling
150has been disabled on this bus).
151
152
153<procedure>(register-method context name msg-cb)</procedure>
154
155Provide a handler (a method body) to be called when the current
156process receives a D-Bus message which matches the given context and
157the given method name.  Start a [[/egg/srfi-18|SRFI-18]] thread to poll for incoming
158messages (unless polling has been disabled on this bus).
159
160
161<procedure>(enable-polling-thread! #!key (bus session-bus) (enable #t) (interval default-polling-interval))</procedure>
162
163Enable or disable the polling thread for a particular bus, and set the
164polling interval in seconds.
165
166
167<procedure>(poll-for-message #!key (bus session-bus) (timeout 0))</procedure>
168
169Check once whether any incoming D-Bus message is waiting on a
170particular bus, and if so, dispatch it to the appropriate callback
171(which you have previously registered using {{register-method}}
172or {{register-signal-handler}}).  Returns {{#t}} if it received a
173message, {{#f}} if not.  The optional {{timeout}} parameter is an
174integer that indicates the maximum time in milliseconds to block waiting
175for messages.
176
177
178<procedure>(discover-services #!key (bus session-bus))</procedure>
179
180Get a list of service names available on a bus.
181
182
183<procedure>(discover-api-xml context)</procedure>
184
185Get the XML API "documentation" provided by the Introspectable
186interface of a service, if that interface is implemented.
187
188
189=== Exported constants
190
191<constant>session-bus</constant>
192
193Session bus.
194
195
196<constant>system-bus</constant>
197
198System bus.
199
200
201<constant>starter-bus</constant>
202
203Starter bus.
204
205
206=== Examples
207
208These are in the examples subdirectory in svn, and included with the egg too.
209
210==== Examples you can test with [[https://en.wikipedia.org/wiki/Qt_(software)|Qt]]
211
212Qt includes a D-Bus [[https://doc.qt.io/qt-6/qtdbus-remotecontrolledcar-example.html|remote-controlled car example]].  E.g. it might be located in {{/usr/share/doc/qt6/examples/dbus/remotecontrolledcar}} depending on your distro.  If you run the car, you can cause the wheels of the car to turn to the right by doing this:
213
214<enscript highlight=scheme>
215(import (prefix dbus dbus:))
216
217(define rc-car-context (dbus:make-context
218        service: 'org.example.CarExample
219        interface: 'org.example.Examples.CarInterface
220        path: '/Car))
221
222(dbus:call rc-car-context "turnRight")
223</enscript>
224
225That example called a method but it did not expect any return values.  But if you are watching with {{dbus-monitor}}, you can see that a response was returned:
226<enscript highlight=c>
227method call time=1625916883.288564 sender=:1.1458 -> destination=org.example.CarExample serial=2 path=/Car; interface=org.example.Examples.CarInterface; member=turnRight
228method return time=1625916883.289087 sender=:1.1455 -> destination=:1.1458 serial=9 reply_serial=2
229</enscript>
230
231Now suppose you want to simulate the car, so you can use the above example to control your own car rather than the Qt one.  And suppose you want to poll for messages manually rather than via the default [[/egg/srfi-18|SRFI-18]] polling thread:
232
233<enscript highlight=scheme>
234(import (prefix dbus dbus:) (chicken format))
235
236(dbus:default-signal-handler (lambda (ctx mber args)
237        ((dbus:printing-signal-handler) ctx mber args)
238        (dbus:dump-callback-table)))
239
240(define (turn-right) (printf "car is turning to the right~%"))
241(define (turn-left) (printf "car is turning to the left~%"))
242
243(define rc-car-context (dbus:make-context
244        service: 'org.example.CarExample
245        path: '/Car
246        interface: 'org.example.Examples.CarInterface ))
247
248(dbus:enable-polling-thread!
249        enable: #f)
250
251(dbus:register-method rc-car-context "turnRight" turn-right)
252(dbus:register-method rc-car-context "turnLeft" turn-left)
253
254(let loop ()
255        (dbus:poll-for-message)
256        (loop))
257</enscript>
258
259{{register-method}} starts a polling loop the first time that it is called with a context specifying a particular bus.  (And if you register methods on multiple buses, there must be a polling thread for each.)  So you can then run the program above which does {{send}}, and you will see the appropriate printf statement execute asynchronously when the message is received.  However the polling thread is subject to the usual limitations of Chicken threads: if there is any blocking I/O, which is waiting for something, then all threads are blocked.  That means you should not use the readline egg for example, because the polling thread will be blocked between each time that you type something and hit Enter.
260
261If the polling thread doesn't work, and you would prefer to poll manually, you can call {{(enable-polling-thread! enable: #f)}} to stop the thread (or to prevent it from being started when you register a new method), and then call {{poll-for-message periodically}}.  Both of those functions can take an optional bus: parameter (which defaults to {{session-bus}}, naturally).  {{poll-for-message}} can also take an optional {{timeout:}} parameter (which is 0 by default, so that it is a non-blocking call).
262
263==== Examples based on the D-Bus Tutorial
264
265The next example, taken from the [[http://dbus.freedesktop.org/doc/dbus/libdbus-tutorial.html|D-Bus tutorial]], shows how to deal with return values.  First the "listener" program which will answer the query:
266
267<enscript highlight=scheme>
268(import (prefix dbus dbus:) (chicken format))
269
270(define (query . params)
271        (printf "got a query; params: ~s~%" params)
272        ;; the response to the query:
273        `(#t 42))
274
275(define ctxt (dbus:make-context
276        service: 'test.method.server
277        interface: 'test.method.Type
278        path: '/test/method/Object))
279
280(dbus:register-method ctxt "Method" query)
281</enscript>
282
283And now the program which sends a query and prints out the response:
284
285<enscript highlight=scheme>
286(import (prefix dbus dbus:) (chicken format))
287
288(define ctxt (dbus:make-context
289        service: 'test.method.server
290        interface: 'test.method.Type
291        path: '/test/method/Object))
292
293(define remote-method (dbus:make-method-proxy ctxt "Method"))
294
295(let ([response (remote-method "query"
296                "What is the meaning of life, the universe and everything?") ])
297
298        (printf "sent a very important query with a known answer; got flippant response ~s~%" response)
299        (if (and (list? response) (eq? 42 (cadr response)))
300                (printf "bingo!~%")
301                (printf "and the answer is wrong too!  Bad supercomputer, bad!~%")))
302</enscript>
303
304What do you get if you combine a remote-controlled mobile thingy with a supercomputer capable of deep thought?  A paranoid android of course!  This example in the examples directory handles both kinds of messages from the two examples above (the query and the turnLeft/turnRight commands) and proves that one polling thread is enough to handle multiple, completely different contexts, on a single bus.  So registering more than one method is low-cost compared to registering the first one.
305
306=== Version History
307
308* 0.86 (2010-08-26)
309* 0.87 (2010-09-04)
310* 0.88 (2012-01-07)
311* 0.89 (2012-02-08)
312* 0.90 (2012-02-09)
313* 0.91 (2012-02-22)
314* 0.92 (2012-10-02)
315* 0.93 (2014-02-09)
316* 0.94 (2016-11-21) compatibility with CHICKEN 4.9.0.1, various fixes
317* 0.95 (2016-11-29) fix compiler warning, fix polling thread lag
318* 0.96 (2019-02-23) ported to Chicken 5; auto-unbox by default; get-property convenience function
Note: See TracBrowser for help on using the repository browser.