em-ruby-dbus 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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