ruby-dbus 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/COPYING +504 -0
  2. data/NEWS +137 -0
  3. data/README +53 -0
  4. data/Rakefile +54 -0
  5. data/VERSION +1 -0
  6. data/doc/tutorial/index.html +356 -0
  7. data/doc/tutorial/index.markdown +467 -0
  8. data/examples/gdbus/gdbus +255 -0
  9. data/examples/gdbus/gdbus.glade +184 -0
  10. data/examples/gdbus/launch.sh +4 -0
  11. data/examples/no-introspect/nm-test.rb +21 -0
  12. data/examples/no-introspect/tracker-test.rb +16 -0
  13. data/examples/rhythmbox/playpause.rb +25 -0
  14. data/examples/service/call_service.rb +25 -0
  15. data/examples/service/service_newapi.rb +51 -0
  16. data/examples/simple/call_introspect.rb +34 -0
  17. data/examples/utils/listnames.rb +11 -0
  18. data/examples/utils/notify.rb +19 -0
  19. data/lib/dbus.rb +91 -0
  20. data/lib/dbus/auth.rb +258 -0
  21. data/lib/dbus/bus.rb +816 -0
  22. data/lib/dbus/core_ext/class/attribute.rb +91 -0
  23. data/lib/dbus/core_ext/kernel/singleton_class.rb +14 -0
  24. data/lib/dbus/core_ext/module/remove_method.rb +12 -0
  25. data/lib/dbus/error.rb +44 -0
  26. data/lib/dbus/export.rb +132 -0
  27. data/lib/dbus/introspect.rb +553 -0
  28. data/lib/dbus/marshall.rb +443 -0
  29. data/lib/dbus/matchrule.rb +100 -0
  30. data/lib/dbus/message.rb +310 -0
  31. data/lib/dbus/type.rb +222 -0
  32. data/ruby-dbus.gemspec +18 -0
  33. data/test/binding_test.rb +56 -0
  34. data/test/bus_driver_test.rb +22 -0
  35. data/test/dbus-launch-simple +35 -0
  36. data/test/dbus-limited-session.conf +28 -0
  37. data/test/server_robustness_test.rb +41 -0
  38. data/test/server_test.rb +53 -0
  39. data/test/service_newapi.rb +129 -0
  40. data/test/session_bus_test_manual.rb +20 -0
  41. data/test/signal_test.rb +64 -0
  42. data/test/t1 +4 -0
  43. data/test/t2.rb +66 -0
  44. data/test/t3-ticket27.rb +18 -0
  45. data/test/t5-report-dbus-interface.rb +58 -0
  46. data/test/t6-loop.rb +82 -0
  47. data/test/test_env +13 -0
  48. data/test/test_server +39 -0
  49. data/test/variant_test.rb +66 -0
  50. metadata +117 -0
@@ -0,0 +1,467 @@
1
+ <style>
2
+ code { background-color: #F0E7E7; }
3
+ pre code { background-color: #F0DDDD; }
4
+ pre {
5
+ font-size: 90%;
6
+ overflow: hidden;
7
+ padding-left: 10pt;
8
+ border: thin solid #F0B4B4;
9
+ background-color: #F0DDDD;
10
+ }
11
+ </style>
12
+
13
+ Welcome
14
+ =======
15
+
16
+ This is the Ruby D-Bus tutorial. It aims to show you the features of Ruby
17
+ D-Bus and as you read through the tutorial also how to use them.
18
+
19
+ &copy; Arnaud Cornet and Paul van Tilburg; this tutorial is part of
20
+ free software; you can redistribute it and/or modify it under the
21
+ terms of the [GNU Lesser General Public License,
22
+ version 2.1](http://www.gnu.org/licenses/lgpl.html) as published by the
23
+ [Free Software Foundation](http://www.fsf.org/).
24
+
25
+ Introduction
26
+ ============
27
+
28
+ This is a tutorial for Ruby D-Bus, a library to access D-Bus facilities of your
29
+ system.
30
+
31
+ What is D-Bus?
32
+ --------------
33
+
34
+ D-Bus is an RPC(Remote Procedure Call) protocol. A common setup can have
35
+ multiple D-Bus daemons running that route procedure calls and signals in
36
+ the form of messages. Each of these daemons supports a bus. A bus that
37
+ is often used by modern desktop environments, and is available per session, is
38
+ called the _session bus_. Another bus that can be available, but in a
39
+ system-wide manner, is called the _system bus_. It is used for example by
40
+ the [Hardware Abstraction Layer](http://hal.freedesktop.org/) daemon. Note
41
+ that theoretically the D-Bus RPC protocol can be used without a system or
42
+ session bus. I never came across any actual use of this though.
43
+
44
+ At the desktop level, D-Bus allows some components to interact. Typically
45
+ if you are writing an application or a personal script that wants to
46
+ interact with your web browser, your music player, or that simply wants to
47
+ pop-up a desktop notification, D-Bus comes into play.
48
+
49
+ At the system level, the Hardware Abstraction Layer is a privileged daemon
50
+ that notifies other software of hardware activities. Typically, if you
51
+ want to be notified if a CD-ROM has been loaded in, of if you want to
52
+ explore hardware, the system daemon comes into play.
53
+
54
+ The D-Bus RPC system is as we will see _object oriented_.
55
+
56
+ Buses provide access to _services_ provided in turn by running or ready to
57
+ run processes. Let me introduce some D-Bus terminology before we discuss
58
+ the API of Ruby D-Bus.
59
+
60
+ Client
61
+ ------
62
+
63
+ A D-Bus client is a process that connects to a D-Bus. They issue method
64
+ calls and register to the bus for signals and events.
65
+
66
+ Service
67
+ -------
68
+
69
+ A connected client can export some of its objects and let other clients
70
+ call some of its methods. Such clients typically register a special name
71
+ like `org.freedesktop.Notifications`, the service name.
72
+
73
+ There is slightly different type of service. They are provided by
74
+ processes that can be launched by a D-Bus daemon on demand. Once they are
75
+ started by D-Bus they register a service name and behave like another
76
+ client.
77
+
78
+ Note that the buses themselves provide the `org.freedesktop.DBus` service,
79
+ and provide some features through it.
80
+
81
+ Object path
82
+ -----------
83
+
84
+ An object path is the D-Bus way to specify an object _instance_ address. A
85
+ service can provide different object instances to the outside world, so
86
+ that external processes can call methods on each of them. An object path
87
+ is an address of an instance in a very similar way that the path is an
88
+ address of a file on a file system. For example:
89
+ `/org/freedesktop/Notification` is an object path of an object provided by
90
+ the `org.freedesktop.Notification` service
91
+
92
+ **Beware**: service names and object paths can, but do _not_ have to be
93
+ related! You'll probably encounter a lot of cases though, where the
94
+ object path is a slashed version of the dotted service name.
95
+
96
+ Interface
97
+ ---------
98
+
99
+ Classically in an object model, classes can implement interfaces. That is,
100
+ some method definitions grouped in an interface. This is exactly what a
101
+ D-Bus interface is as well. In D-Bus interfaces have names. These names must be
102
+ specified on method calls.
103
+
104
+ The `org.freedesktop.Notification` service provides an object instance
105
+ called `/org/freedesktop/Notification`. This instance object implements an
106
+ interface called `org.freedesktop.Notifications`. It also provides two
107
+ special D-Bus specific interfaces: `org.freedesktop.DBus.Introspect` and
108
+ `org.freedesktop.DBus.Properties`. Again, object paths, service names,
109
+ and interface names can be related but do not have to be.
110
+
111
+ Basically the `org.freedesktop.DBus.Introspect` has an `Introspect` method,
112
+ that returns XML data describing the `/org/freedesktop/Notification` object
113
+ interfaces. This is used heavily internally by Ruby D-Bus.
114
+
115
+ Method
116
+ ------
117
+
118
+ A method is, well, a method in the classical meaning. It's a function that
119
+ is called in the context of an object instance. Methods have typed
120
+ parameters and return typed return values.
121
+
122
+ Signal
123
+ ------
124
+
125
+ Signals are simplified method calls that do not have a return value. They
126
+ do have typed parameters though.
127
+
128
+ Message
129
+ -------
130
+
131
+ Method calls, method returns, signals, errors: all are encoded as D-Bus
132
+ messages sent over a bus. They are made of a packet header with source and
133
+ destination address, a type (method call, method reply, signal) and the
134
+ body containing the parameters (for signals and method calls) or the return
135
+ values (for a method return message).
136
+
137
+ Signature
138
+ ---------
139
+
140
+ Because D-Bus is typed and dynamic, each message comes with a signature that
141
+ describes the types of the data that is contained within the message. The
142
+ signature is a string with an extremely basic language that only describes
143
+ a data type. You will need to have some knowledge of what a signature
144
+ looks like if you are setting up a service. If you are just programming a
145
+ D-Bus client, you can live without knowing about them.
146
+
147
+ Client Usage
148
+ ============
149
+
150
+ This chapter discusses basic client usage
151
+ and has the following topics:
152
+
153
+ Using the library
154
+ -----------------
155
+
156
+ If you want to use the library, you have to make Ruby load it by issuing:
157
+
158
+ require 'dbus'
159
+
160
+ That's all! Now we can move on to really using it...
161
+
162
+ Connecting to a bus
163
+ -------------------
164
+
165
+ On a typical system, two buses are running, the system bus and the session
166
+ bus. The system bus can be accessed by:
167
+
168
+ bus = DBus::SystemBus.instance
169
+
170
+ Probably you already have guessed how to access the session bus. This
171
+ can be done by:
172
+
173
+ bus = DBus::SessionBus.instance
174
+
175
+ Performing method calls
176
+ -----------------------
177
+
178
+ Let me continue this example using the session bus. Let's say that I want
179
+ to access an object of some client on the session bus. This particular
180
+ D-Bus client provides a service called `org.gnome.Rhythmbox`. Let me
181
+ access this service:
182
+
183
+ rb_service = bus.service("org.gnome.Rhythmbox")
184
+
185
+ In this example I access the `org.gnome.Rhythmbox` service, which is
186
+ provided by the application
187
+ [Rhythmbox](http://www.gnome.org/projects/rhythmbox/).
188
+ OK, I have a service handle now, and I know that it exports the object
189
+ "/org/gnome/Rhythmbox/Player". I will trivially access this remote object
190
+ using:
191
+
192
+ rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
193
+
194
+ Introspection
195
+ -------------
196
+
197
+ Well, that was easy. Let's say that I know that this particular object is
198
+ introspectable. In real life most of them are. The `rb_object` object we
199
+ have here is just a handle of a remote object, in general they are called
200
+ _proxy objects_, because they are the local handle of a remote object. It
201
+ would be nice to be able to make it have methods, and that its methods send
202
+ a D-Bus call to remotely execute the actual method in another process.
203
+ Well, instating these methods for a _introspectable_ object is trivial:
204
+
205
+ rb_player.introspect
206
+
207
+ And there you go. Note that not all services or objects can be
208
+ introspected, therefore you have to do this manually! Let me remind you
209
+ that objects in D-Bus have interfaces and interfaces have methods. Let's
210
+ now access these methods:
211
+
212
+ rb_player_iface = rb_player["org.gnome.Rhythmbox.Player"]
213
+ puts rb_player_iface.getPlayingUri
214
+
215
+ As you can see, when you want to call a method on an instance object, you have
216
+ to get the correct interface. It is a bit tedious, so we have the following
217
+ shortcut that does the same thing as before:
218
+
219
+ rb_player.default_iface = "org.gnome.Rhythmbox.Player"
220
+ puts rb_player.getPlayingUri
221
+
222
+ The `default_iface=` call specifies the default interface that should be
223
+ used when non existing methods are called directly on a proxy object, and
224
+ not on one of its interfaces.
225
+
226
+ Note that the bus itself has a corresponding introspectable object. You can
227
+ access it with `bus.proxy` method. For example, you can retrieve an array of
228
+ exported service names of a bus like this:
229
+
230
+ bus.proxy.ListNames[0]
231
+
232
+ Calling a method asynchronously
233
+ -------------------------------
234
+
235
+ D-Bus is _asynchronous_. This means that you do not have to wait for a
236
+ reply when you send a message. When you call a remote method that takes a
237
+ lot of time to process remotely, you don't want your application to hang,
238
+ right? Well the asychronousness exists for this reason. What if you dont'
239
+ want to wait for the return value of a method, but still you want to take
240
+ some action when you receive it?
241
+
242
+ There is a classical method to program this event-driven mechanism. You do
243
+ some computation, perform some method call, and at the same time you setup
244
+ a callback that will be triggered once you receive a reply. Then you run a
245
+ main loop that is responsible to call the callbacks properly. Here is how
246
+ you do it:
247
+
248
+ rb_player.getPlayingUri do |resp|
249
+ puts "The playing URI is #{resp}"
250
+ end
251
+ puts "See, I'm not waiting!"
252
+ loop = DBus::Main.new
253
+ loop << bus
254
+ loop.run
255
+
256
+ This code will print the following:
257
+
258
+ See, I'm not waiting!
259
+ The playing URI is file:///music/papapingoin.mp3
260
+
261
+ Waiting for a signal
262
+ --------------------
263
+
264
+ Signals are calls from the remote object to your program. As a client, you
265
+ set yourself up to receive a signal and handle it with a callback. Then running
266
+ the main loop triggers the callback. You can register a callback handler
267
+ as allows:
268
+
269
+ rb_player.on_signal("elapsedChanged") do |u|
270
+ puts u
271
+ end
272
+
273
+ More about introspection
274
+ ------------------------
275
+
276
+ There are various ways to inspect a remote service. You can simply call
277
+ `Introspect()` and read the XML output. However, in this tutorial I assume
278
+ that you want to do it using the Ruby D-Bus API.
279
+
280
+ Notice that you can introspect a service, and not only objects:
281
+
282
+ rb_service = bus.service("org.gnome.Rhythmbox")
283
+ rb_service.introspect
284
+ p rb_service.root
285
+
286
+ This dumps a tree-like structure that represents multiple object paths. In
287
+ this particular case the output is:
288
+
289
+ </: {org => {gnome => {Rhythmbox => {Player => ..fdbe625de {},Shell => ..fdbe6852e {},PlaylistManager => ..fdbe4e340 {}}>
290
+
291
+ Read this left to right: the root node is "/", it has one child node "org",
292
+ "org" has one child node "gnome", and "gnome" has one child node "Rhythmbox".
293
+ Rhythmbox has Tree child nodes "Player", "Shell" and "PlaylistManager".
294
+ These three last child nodes have a weird digit that means it has an object
295
+ instance. Such object instances are already introspected.
296
+
297
+ If the prose wasn't clear, maybe the following ASCII art will help you:
298
+
299
+ /
300
+ org
301
+ gnome
302
+ Rhythmbox
303
+ Shell (with object)
304
+ Player (with object)
305
+ PlaylistManager (with object)
306
+
307
+ ### Walking the object tree
308
+
309
+ You can have an object on any node, i.e. it is not limited to leaves.
310
+ You can access a specific node like this:
311
+
312
+ rb_player = rb_service.root["org"]["gnome"]["Rhythmbox"]["Player"]
313
+ rb_player = rb_service.object("/org/gnome/Rhythmbox/Player")
314
+
315
+ The difference between the two is that for the first one, `rb_service`
316
+ needs to have been introspected. Also the obtained `rb_player` is already
317
+ introspected whereas the second `rb_player` isn't yet.
318
+
319
+ Errors
320
+ ------
321
+
322
+ D-Bus calls can reply with an error instead of a return value. An error is
323
+ translated to a Ruby exception.
324
+
325
+ begin
326
+ network_manager.sleep
327
+ rescue DBus::Error => e
328
+ puts e unless e.name == "org.freedesktop.NetworkManager.AlreadyAsleepOrAwake"
329
+ end
330
+
331
+ Creating a Service
332
+ ==================
333
+
334
+ This chapter deals with the opposite side of the basic client usage, namely
335
+ the creation of a D-Bus service.
336
+
337
+ Registering a service
338
+ ---------------------
339
+
340
+ Now that you know how to perform D-Bus calls, and how to wait for and
341
+ handle signals, you might want to learn how to publish some object and
342
+ interface to provide them to the D-Bus world. Here is how you do that.
343
+
344
+ As you should already know, D-Bus clients that provide some object to be
345
+ called remotely are services. Here is how to allocate a name on a bus:
346
+
347
+ bus = DBus.session_bus
348
+ service = bus.request_service("org.ruby.service")
349
+
350
+ Now this client is know to the outside world as `org.ruby.service`.
351
+ Note that this is a request and it _can_ be denied! When it
352
+ is denied, an exception (`DBus::NameRequestError`) is thrown.
353
+
354
+ Exporting an object
355
+ -------------------
356
+
357
+ Now, let's define a class that we want to export:
358
+
359
+ class Test < DBus::Object
360
+ # Create an interface.
361
+ dbus_interface "org.ruby.SampleInterface" do
362
+ # Create a hello method in that interface.
363
+ dbus_method :hello, "in name:s, in name2:s" do |name, name2|
364
+ puts "hello(#{name}, #{name2})"
365
+ end
366
+ end
367
+ end
368
+
369
+ As you can see, we define a `Test` class in which we define a
370
+ `org.ruby.SampleInterface` interface. In this interface, we define a
371
+ method. The given code block is the method's implementation. This will be
372
+ executed when remote programs performs a D-Bus call. Now the annoying part:
373
+ the actual method definition. As you can guess the call
374
+
375
+ dbus_method :hello, "in name:s, in name2:s" do ...
376
+
377
+ creates a `hello` method that takes two parameters both of type string.
378
+ The _:s_ means "of type string". Let's have a look at some other common
379
+ parameter types:
380
+
381
+ - *u* means unsigned integer
382
+ - *i* means integer
383
+ - *y* means byte
384
+ - *(ui)* means a structure having a unsigned integer and a signed one.
385
+ - *a* means array, so that "ai" means array of integers
386
+ - *as* means array of string
387
+ - *a(is)* means array of structures, each having an integer and a string.
388
+
389
+ For a full description of the available D-Bus types, please refer to the
390
+ [D-Bus specification](http://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-signatures).
391
+
392
+ Now that the class has been defined, we can instantiate an object
393
+ and export it as follows:
394
+
395
+ exported_obj = Test.new("/org/ruby/MyInstance")
396
+ service.export(exported_obj)
397
+
398
+ This piece of code above instantiates a `Test` object with a D-Bus object
399
+ path. This object is reachable from the outside world after
400
+ `service.export(exported_obj)` is called.
401
+
402
+ We also need a loop which will read and process the calls coming over the bus:
403
+
404
+ loop = DBus::Main.new
405
+ loop << bus
406
+ loop.run
407
+
408
+ ### Using the exported object
409
+
410
+ Now, let's consider another program that will access our newly created service:
411
+
412
+ ruby_service = bus.service("org.ruby.service")
413
+ obj = ruby_service.object("/org/ruby/MyInstance")
414
+ obj.introspect
415
+ obj.default_iface = "org.ruby.SampleInterface"
416
+ obj.hello("giligiligiligili", "haaaaaaa")
417
+
418
+ As you can see, the object we defined earlier is automatically introspectable.
419
+ See also "Basic Client Usage".
420
+
421
+ Emitting a signal
422
+ -----------------
423
+
424
+ Let's add some example method so you can see how to return a value to the
425
+ caller and let's also define another example interface that has a signal.
426
+
427
+ class Test2 < DBus::Object
428
+ # Create an interface
429
+ dbus_interface "org.ruby.SampleInterface" do
430
+ # Create a hello method in the interface:
431
+ dbus_method :hello, "in name:s, in name2:s" do |name, name2|
432
+ puts "hello(#{name}, #{name2})"
433
+ end
434
+ # Define a signal in the interface:
435
+ dbus_signal :SomethingJustHappened, "toto:s, tutu:u"
436
+ end
437
+
438
+ dbus_interface "org.ruby.AnotherInterface" do
439
+ dbus_method :ThatsALongMethodNameIThink, "in name:s, out ret:s" do |name|
440
+ ["So your name is #{name}"]
441
+ end
442
+ end
443
+ end
444
+
445
+ Triggering the signal is a easy as calling a method, but then this time on
446
+ a local (exported) object and not on a remote/proxy object:
447
+
448
+ exported_obj.SomethingJustHappened("blah", 1)
449
+
450
+ Note that the `ThatsALongMethodNameIThink` method is returning a single
451
+ value to the caller. Notice that you always have to return an array. If
452
+ you want to return multiple values, just have an array with multiple
453
+ values.
454
+
455
+ Replying with an error
456
+ ----------------------
457
+
458
+ To reply to a dbus_method with a D-Bus error, raise a `DBus::Error`,
459
+ as constructed by the `error` convenience function:
460
+
461
+ raise DBus.error("org.example.Error.SeatOccupied"), "Seat #{seat} is occupied"
462
+
463
+ If the error name is not specified, the generic
464
+ `org.freedesktop.DBus.Error.Failed` is used.
465
+
466
+ raise DBus.error, "Seat #{seat} is occupied"
467
+ raise DBus.error