em-ruby-dbus 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/COPYING +504 -0
  3. data/NEWS +253 -0
  4. data/README.md +93 -0
  5. data/Rakefile +58 -0
  6. data/VERSION +1 -0
  7. data/doc/Reference.md +207 -0
  8. data/doc/Tutorial.md +480 -0
  9. data/doc/ex-calling-methods.body.rb +8 -0
  10. data/doc/ex-calling-methods.rb +3 -0
  11. data/doc/ex-properties.body.rb +9 -0
  12. data/doc/ex-properties.rb +3 -0
  13. data/doc/ex-setup.rb +7 -0
  14. data/doc/ex-signal.body.rb +20 -0
  15. data/doc/ex-signal.rb +3 -0
  16. data/doc/example-helper.rb +6 -0
  17. data/em-ruby-dbus.gemspec +20 -0
  18. data/examples/gdbus/gdbus +255 -0
  19. data/examples/gdbus/gdbus.glade +184 -0
  20. data/examples/gdbus/launch.sh +4 -0
  21. data/examples/no-introspect/nm-test.rb +21 -0
  22. data/examples/no-introspect/tracker-test.rb +16 -0
  23. data/examples/rhythmbox/playpause.rb +25 -0
  24. data/examples/service/call_service.rb +25 -0
  25. data/examples/service/service_newapi.rb +51 -0
  26. data/examples/simple/call_introspect.rb +34 -0
  27. data/examples/simple/properties.rb +19 -0
  28. data/examples/utils/listnames.rb +11 -0
  29. data/examples/utils/notify.rb +19 -0
  30. data/lib/dbus.rb +82 -0
  31. data/lib/dbus/auth.rb +269 -0
  32. data/lib/dbus/bus.rb +739 -0
  33. data/lib/dbus/core_ext/array/extract_options.rb +31 -0
  34. data/lib/dbus/core_ext/class/attribute.rb +129 -0
  35. data/lib/dbus/core_ext/kernel/singleton_class.rb +8 -0
  36. data/lib/dbus/core_ext/module/remove_method.rb +14 -0
  37. data/lib/dbus/error.rb +46 -0
  38. data/lib/dbus/export.rb +128 -0
  39. data/lib/dbus/introspect.rb +219 -0
  40. data/lib/dbus/logger.rb +31 -0
  41. data/lib/dbus/loop-em.rb +19 -0
  42. data/lib/dbus/marshall.rb +434 -0
  43. data/lib/dbus/matchrule.rb +101 -0
  44. data/lib/dbus/message.rb +276 -0
  45. data/lib/dbus/message_queue.rb +166 -0
  46. data/lib/dbus/proxy_object.rb +149 -0
  47. data/lib/dbus/proxy_object_factory.rb +41 -0
  48. data/lib/dbus/proxy_object_interface.rb +128 -0
  49. data/lib/dbus/type.rb +193 -0
  50. data/lib/dbus/xml.rb +161 -0
  51. data/test/async_spec.rb +47 -0
  52. data/test/binding_spec.rb +74 -0
  53. data/test/bus_and_xml_backend_spec.rb +39 -0
  54. data/test/bus_driver_spec.rb +20 -0
  55. data/test/bus_spec.rb +20 -0
  56. data/test/byte_array_spec.rb +38 -0
  57. data/test/err_msg_spec.rb +42 -0
  58. data/test/introspect_xml_parser_spec.rb +26 -0
  59. data/test/introspection_spec.rb +32 -0
  60. data/test/main_loop_spec.rb +82 -0
  61. data/test/property_spec.rb +53 -0
  62. data/test/server_robustness_spec.rb +66 -0
  63. data/test/server_spec.rb +53 -0
  64. data/test/service_newapi.rb +217 -0
  65. data/test/session_bus_spec_manual.rb +15 -0
  66. data/test/signal_spec.rb +90 -0
  67. data/test/spec_helper.rb +33 -0
  68. data/test/thread_safety_spec.rb +31 -0
  69. data/test/tools/dbus-launch-simple +35 -0
  70. data/test/tools/dbus-limited-session.conf +28 -0
  71. data/test/tools/test_env +13 -0
  72. data/test/tools/test_server +39 -0
  73. data/test/type_spec.rb +19 -0
  74. data/test/value_spec.rb +81 -0
  75. data/test/variant_spec.rb +66 -0
  76. metadata +145 -0
@@ -0,0 +1,31 @@
1
+ # dbus/logger.rb - debug logging
2
+ #
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2012 Martin Vidner
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ require 'logger'
12
+
13
+ module DBus
14
+ # Get the logger for the DBus module.
15
+ # The default one logs to STDERR,
16
+ # with DEBUG if $DEBUG is set, otherwise INFO.
17
+ def logger
18
+ unless defined? @logger
19
+ @logger = Logger.new(STDERR)
20
+ @logger.level = $DEBUG ? Logger::DEBUG : Logger::INFO
21
+ end
22
+ @logger
23
+ end
24
+ module_function :logger
25
+
26
+ # Set the logger for the DBus module
27
+ def logger=(logger)
28
+ @logger = logger
29
+ end
30
+ module_function :logger=
31
+ end
@@ -0,0 +1,19 @@
1
+ require 'eventmachine'
2
+
3
+ module DBus
4
+ module Loop
5
+ module EventMachine
6
+ class Reader < ::EventMachine::Connection
7
+ def initialize(parent)
8
+ @parent = parent
9
+ end
10
+
11
+ def notify_readable
12
+ @parent.dispatch_message_queue
13
+ rescue EOFError
14
+ detach
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,434 @@
1
+ # dbus.rb - Module containing the low-level D-Bus implementation
2
+ #
3
+ # This file is part of the ruby-dbus project
4
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
5
+ #
6
+ # This library is free software; you can redistribute it and/or
7
+ # modify it under the terms of the GNU Lesser General Public
8
+ # License, version 2.1 as published by the Free Software Foundation.
9
+ # See the file "COPYING" for the exact licensing terms.
10
+
11
+ require 'socket'
12
+
13
+ # = D-Bus main module
14
+ #
15
+ # Module containing all the D-Bus modules and classes.
16
+ module DBus
17
+ # Exception raised when an invalid packet is encountered.
18
+ class InvalidPacketException < Exception
19
+ end
20
+
21
+ # = D-Bus packet unmarshaller class
22
+ #
23
+ # Class that handles the conversion (unmarshalling) of payload data
24
+ # to Array.
25
+ class PacketUnmarshaller
26
+ # Index pointer that points to the byte in the data that is
27
+ # currently being processed.
28
+ #
29
+ # Used to kown what part of the buffer has been consumed by unmarshalling.
30
+ # FIXME: Maybe should be accessed with a "consumed_size" method.
31
+ attr_reader :idx
32
+
33
+ # Create a new unmarshaller for the given data _buffer_ and _endianness_.
34
+ def initialize(buffer, endianness)
35
+ @buffy, @endianness = buffer.dup, endianness
36
+ if @endianness == BIG_END
37
+ @uint32 = "N"
38
+ @uint16 = "n"
39
+ @double = "G"
40
+ elsif @endianness == LIL_END
41
+ @uint32 = "V"
42
+ @uint16 = "v"
43
+ @double = "E"
44
+ else
45
+ raise InvalidPacketException, "Incorrect endianness #{@endianness}"
46
+ end
47
+ @idx = 0
48
+ end
49
+
50
+ # Unmarshall the buffer for a given _signature_ and length _len_.
51
+ # Return an array of unmarshalled objects
52
+ def unmarshall(signature, len = nil)
53
+ if len != nil
54
+ if @buffy.bytesize < @idx + len
55
+ raise IncompleteBufferException
56
+ end
57
+ end
58
+ sigtree = Type::Parser.new(signature).parse
59
+ ret = Array.new
60
+ sigtree.each do |elem|
61
+ ret << do_parse(elem)
62
+ end
63
+ ret
64
+ end
65
+
66
+ # Align the pointer index on a byte index of _a_, where a
67
+ # must be 1, 2, 4 or 8.
68
+ def align(a)
69
+ case a
70
+ when 1
71
+ when 2, 4, 8
72
+ bits = a - 1
73
+ @idx = @idx + bits & ~bits
74
+ raise IncompleteBufferException if @idx > @buffy.bytesize
75
+ else
76
+ raise "Unsupported alignment #{a}"
77
+ end
78
+ end
79
+
80
+ ###############################################################
81
+ # FIXME: does anyone except the object itself call the above methods?
82
+ # Yes : Message marshalling code needs to align "body" to 8 byte boundary
83
+ private
84
+
85
+ # Retrieve the next _nbytes_ number of bytes from the buffer.
86
+ def get(nbytes)
87
+ raise IncompleteBufferException if @idx + nbytes > @buffy.bytesize
88
+ ret = @buffy.slice(@idx, nbytes)
89
+ @idx += nbytes
90
+ ret
91
+ end
92
+
93
+ # Get the string length and string itself from the buffer.
94
+ # Return the string.
95
+ def get_string
96
+ align(4)
97
+ str_sz = get(4).unpack(@uint32)[0]
98
+ ret = @buffy.slice(@idx, str_sz)
99
+ raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.bytesize
100
+ @idx += str_sz
101
+ if @buffy[@idx].ord != 0
102
+ raise InvalidPacketException, "String is not nul-terminated"
103
+ end
104
+ @idx += 1
105
+ # no exception, see check above
106
+ ret
107
+ end
108
+
109
+ # Get the signature length and signature itself from the buffer.
110
+ # Return the signature.
111
+ def get_signature
112
+ str_sz = get(1).unpack('C')[0]
113
+ ret = @buffy.slice(@idx, str_sz)
114
+ raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.bytesize
115
+ @idx += str_sz
116
+ if @buffy[@idx].ord != 0
117
+ raise InvalidPacketException, "Type is not nul-terminated"
118
+ end
119
+ @idx += 1
120
+ # no exception, see check above
121
+ ret
122
+ end
123
+
124
+ # Based on the _signature_ type, retrieve a packet from the buffer
125
+ # and return it.
126
+ def do_parse(signature)
127
+ packet = nil
128
+ case signature.sigtype
129
+ when Type::BYTE
130
+ packet = get(1).unpack("C")[0]
131
+ when Type::UINT16
132
+ align(2)
133
+ packet = get(2).unpack(@uint16)[0]
134
+ when Type::INT16
135
+ align(4)
136
+ packet = get(4).unpack(@uint16)[0]
137
+ if (packet & 0x8000) != 0
138
+ packet -= 0x10000
139
+ end
140
+ when Type::UINT32, Type::UNIX_FD
141
+ align(4)
142
+ packet = get(4).unpack(@uint32)[0]
143
+ when Type::INT32
144
+ align(4)
145
+ packet = get(4).unpack(@uint32)[0]
146
+ if (packet & 0x80000000) != 0
147
+ packet -= 0x100000000
148
+ end
149
+ when Type::UINT64
150
+ align(8)
151
+ packet_l = get(4).unpack(@uint32)[0]
152
+ packet_h = get(4).unpack(@uint32)[0]
153
+ if @endianness == LIL_END
154
+ packet = packet_l + packet_h * 2**32
155
+ else
156
+ packet = packet_l * 2**32 + packet_h
157
+ end
158
+ when Type::INT64
159
+ align(8)
160
+ packet_l = get(4).unpack(@uint32)[0]
161
+ packet_h = get(4).unpack(@uint32)[0]
162
+ if @endianness == LIL_END
163
+ packet = packet_l + packet_h * 2**32
164
+ else
165
+ packet = packet_l * 2**32 + packet_h
166
+ end
167
+ if (packet & 0x8000000000000000) != 0
168
+ packet -= 0x10000000000000000
169
+ end
170
+ when Type::DOUBLE
171
+ align(8)
172
+ packet = get(8).unpack(@double)[0]
173
+ when Type::BOOLEAN
174
+ align(4)
175
+ v = get(4).unpack(@uint32)[0]
176
+ raise InvalidPacketException if not [0, 1].member?(v)
177
+ packet = (v == 1)
178
+ when Type::ARRAY
179
+ align(4)
180
+ # checks please
181
+ array_sz = get(4).unpack(@uint32)[0]
182
+ raise InvalidPacketException if array_sz > 67108864
183
+
184
+ align(signature.child.alignment)
185
+ raise IncompleteBufferException if @idx + array_sz > @buffy.bytesize
186
+
187
+ packet = Array.new
188
+ start_idx = @idx
189
+ while @idx - start_idx < array_sz
190
+ packet << do_parse(signature.child)
191
+ end
192
+
193
+ if signature.child.sigtype == Type::DICT_ENTRY then
194
+ packet = packet.inject(Hash.new) do |hash, pair|
195
+ hash[pair[0]] = pair[1]
196
+ hash
197
+ end
198
+ end
199
+ when Type::STRUCT
200
+ align(8)
201
+ packet = Array.new
202
+ signature.members.each do |elem|
203
+ packet << do_parse(elem)
204
+ end
205
+ when Type::VARIANT
206
+ string = get_signature
207
+ # error checking please
208
+ sig = Type::Parser.new(string).parse[0]
209
+ align(sig.alignment)
210
+ packet = do_parse(sig)
211
+ when Type::OBJECT_PATH
212
+ packet = get_string
213
+ when Type::STRING
214
+ packet = get_string
215
+ packet.force_encoding('UTF-8')
216
+ when Type::SIGNATURE
217
+ packet = get_signature
218
+ when Type::DICT_ENTRY
219
+ align(8)
220
+ key = do_parse(signature.members[0])
221
+ value = do_parse(signature.members[1])
222
+ packet = [key, value]
223
+ else
224
+ raise NotImplementedError,
225
+ "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
226
+ end
227
+ packet
228
+ end # def do_parse
229
+ end # class PacketUnmarshaller
230
+
231
+ # D-Bus packet marshaller class
232
+ #
233
+ # Class that handles the conversion (marshalling) of Ruby objects to
234
+ # (binary) payload data.
235
+ class PacketMarshaller
236
+ # The current or result packet.
237
+ # FIXME: allow access only when marshalling is finished
238
+ attr_reader :packet
239
+
240
+ # Create a new marshaller, setting the current packet to the
241
+ # empty packet.
242
+ def initialize(offset = 0)
243
+ @packet = ""
244
+ @offset = offset # for correct alignment of nested marshallers
245
+ end
246
+
247
+ # Round _n_ up to the specified power of two, _a_
248
+ def num_align(n, a)
249
+ case a
250
+ when 1, 2, 4, 8
251
+ bits = a - 1
252
+ n + bits & ~bits
253
+ else
254
+ raise "Unsupported alignment"
255
+ end
256
+ end
257
+
258
+ # Align the buffer with NULL (\0) bytes on a byte length of _a_.
259
+ def align(a)
260
+ @packet = @packet.ljust(num_align(@offset + @packet.bytesize, a) - @offset, 0.chr)
261
+ end
262
+
263
+ # Append the the string _str_ itself to the packet.
264
+ def append_string(str)
265
+ align(4)
266
+ @packet += [str.bytesize].pack("L") + [str].pack("Z*")
267
+ end
268
+
269
+ # Append the the signature _signature_ itself to the packet.
270
+ def append_signature(str)
271
+ @packet += str.bytesize.chr + str + "\0"
272
+ end
273
+
274
+ # Append the array type _type_ to the packet and allow for appending
275
+ # the child elements.
276
+ def array(type)
277
+ # Thanks to Peter Rullmann for this line
278
+ align(4)
279
+ sizeidx = @packet.bytesize
280
+ @packet += "ABCD"
281
+ align(type.alignment)
282
+ contentidx = @packet.bytesize
283
+ yield
284
+ sz = @packet.bytesize - contentidx
285
+ raise InvalidPacketException if sz > 67108864
286
+ @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
287
+ end
288
+
289
+ # Align and allow for appending struct fields.
290
+ def struct
291
+ align(8)
292
+ yield
293
+ end
294
+
295
+ # Append a value _val_ to the packet based on its _type_.
296
+ #
297
+ # Host native endianness is used, declared in Message#marshall
298
+ def append(type, val)
299
+ raise TypeException, "Cannot send nil" if val.nil?
300
+
301
+ type = type.chr if type.kind_of?(Fixnum)
302
+ type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
303
+ case type.sigtype
304
+ when Type::BYTE
305
+ @packet += val.chr
306
+ when Type::UINT32, Type::UNIX_FD
307
+ align(4)
308
+ @packet += [val].pack("L")
309
+ when Type::UINT64
310
+ align(8)
311
+ @packet += [val].pack("Q")
312
+ when Type::INT64
313
+ align(8)
314
+ @packet += [val].pack("q")
315
+ when Type::INT32
316
+ align(4)
317
+ @packet += [val].pack("l")
318
+ when Type::UINT16
319
+ align(2)
320
+ @packet += [val].pack("S")
321
+ when Type::INT16
322
+ align(2)
323
+ @packet += [val].pack("s")
324
+ when Type::DOUBLE
325
+ align(8)
326
+ @packet += [val].pack("d")
327
+ when Type::BOOLEAN
328
+ align(4)
329
+ if val
330
+ @packet += [1].pack("L")
331
+ else
332
+ @packet += [0].pack("L")
333
+ end
334
+ when Type::OBJECT_PATH
335
+ append_string(val)
336
+ when Type::STRING
337
+ append_string(val)
338
+ when Type::SIGNATURE
339
+ append_signature(val)
340
+ when Type::VARIANT
341
+ vartype = nil
342
+ if val.is_a?(Array) and val.size == 2
343
+ if val[0].is_a?(DBus::Type::Type)
344
+ vartype, vardata = val
345
+ elsif val[0].is_a?(String)
346
+ begin
347
+ parsed = Type::Parser.new(val[0]).parse
348
+ vartype = parsed[0] if parsed.size == 1
349
+ vardata = val[1]
350
+ rescue Type::SignatureException
351
+ # no assignment
352
+ end
353
+ end
354
+ end
355
+ if vartype.nil?
356
+ vartype, vardata = PacketMarshaller.make_variant(val)
357
+ vartype = Type::Parser.new(vartype).parse[0]
358
+ end
359
+
360
+ append_signature(vartype.to_s)
361
+ align(vartype.alignment)
362
+ sub = PacketMarshaller.new(@offset + @packet.bytesize)
363
+ sub.append(vartype, vardata)
364
+ @packet += sub.packet
365
+ when Type::ARRAY
366
+ if val.kind_of?(Hash)
367
+ raise TypeException, "Expected an Array but got a Hash" if type.child.sigtype != Type::DICT_ENTRY
368
+ # Damn ruby rocks here
369
+ val = val.to_a
370
+ end
371
+ # If string is recieved and ay is expected, explode the string
372
+ if val.kind_of?(String) && type.child.sigtype == Type::BYTE
373
+ val = val.bytes
374
+ end
375
+ if not val.kind_of?(Enumerable)
376
+ raise TypeException, "Expected an Enumerable of #{type.child.inspect} but got a #{val.class}"
377
+ end
378
+ array(type.child) do
379
+ val.each do |elem|
380
+ append(type.child, elem)
381
+ end
382
+ end
383
+ when Type::STRUCT, Type::DICT_ENTRY
384
+ # TODO use duck typing, val.respond_to?
385
+ raise TypeException, "Struct/DE expects an Array" if not val.kind_of?(Array)
386
+ if type.sigtype == Type::DICT_ENTRY and val.size != 2
387
+ raise TypeException, "Dict entry expects a pair"
388
+ end
389
+ if type.members.size != val.size
390
+ raise TypeException, "Struct/DE has #{val.size} elements but type info for #{type.members.size}"
391
+ end
392
+ struct do
393
+ type.members.zip(val).each do |t, v|
394
+ append(t, v)
395
+ end
396
+ end
397
+ else
398
+ raise NotImplementedError,
399
+ "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
400
+ end
401
+ end # def append
402
+
403
+ # Make a [signature, value] pair for a variant
404
+ def self.make_variant(value)
405
+ # TODO: mix in _make_variant to String, Integer...
406
+ if value == true
407
+ ["b", true]
408
+ elsif value == false
409
+ ["b", false]
410
+ elsif value.nil?
411
+ ["b", nil]
412
+ elsif value.is_a? Float
413
+ ["d", value]
414
+ elsif value.is_a? Symbol
415
+ ["s", value.to_s]
416
+ elsif value.is_a? Array
417
+ ["av", value.map {|i| make_variant(i) }]
418
+ elsif value.is_a? Hash
419
+ h = {}
420
+ value.each_key {|k| h[k] = make_variant(value[k]) }
421
+ ["a{sv}", h]
422
+ elsif value.respond_to? :to_str
423
+ ["s", value.to_str]
424
+ elsif value.respond_to? :to_int
425
+ i = value.to_int
426
+ if -2_147_483_648 <= i && i < 2_147_483_648
427
+ ["i", i]
428
+ else
429
+ ["x", i]
430
+ end
431
+ end
432
+ end
433
+ end # class PacketMarshaller
434
+ end # module DBus