ruby-dbus-openplacos 0.6.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 (51) hide show
  1. data/COPYING +504 -0
  2. data/NEWS +146 -0
  3. data/README +42 -0
  4. data/Rakefile +54 -0
  5. data/VERSION +1 -0
  6. data/doc/tutorial/index.markdown +480 -0
  7. data/examples/gdbus/gdbus +255 -0
  8. data/examples/gdbus/gdbus.glade +184 -0
  9. data/examples/gdbus/launch.sh +4 -0
  10. data/examples/no-introspect/nm-test.rb +21 -0
  11. data/examples/no-introspect/tracker-test.rb +16 -0
  12. data/examples/rhythmbox/playpause.rb +25 -0
  13. data/examples/service/call_service.rb +25 -0
  14. data/examples/service/service_newapi.rb +51 -0
  15. data/examples/simple/call_introspect.rb +34 -0
  16. data/examples/simple/properties.rb +19 -0
  17. data/examples/utils/listnames.rb +11 -0
  18. data/examples/utils/notify.rb +19 -0
  19. data/lib/dbus/auth.rb +258 -0
  20. data/lib/dbus/bus.rb +947 -0
  21. data/lib/dbus/core_ext/class/attribute.rb +91 -0
  22. data/lib/dbus/core_ext/kernel/singleton_class.rb +14 -0
  23. data/lib/dbus/core_ext/module/remove_method.rb +12 -0
  24. data/lib/dbus/error.rb +44 -0
  25. data/lib/dbus/export.rb +124 -0
  26. data/lib/dbus/introspect.rb +570 -0
  27. data/lib/dbus/marshall.rb +443 -0
  28. data/lib/dbus/matchrule.rb +100 -0
  29. data/lib/dbus/message.rb +310 -0
  30. data/lib/dbus/type.rb +222 -0
  31. data/lib/dbus.rb +83 -0
  32. data/ruby-dbus-openplacos.gemspec +17 -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/property_test.rb +55 -0
  38. data/test/server_robustness_test.rb +72 -0
  39. data/test/server_test.rb +53 -0
  40. data/test/service_newapi.rb +197 -0
  41. data/test/session_bus_test_manual.rb +20 -0
  42. data/test/signal_test.rb +64 -0
  43. data/test/t1 +4 -0
  44. data/test/t2.rb +66 -0
  45. data/test/t3-ticket27.rb +18 -0
  46. data/test/t5-report-dbus-interface.rb +58 -0
  47. data/test/t6-loop.rb +82 -0
  48. data/test/test_env +13 -0
  49. data/test/test_server +39 -0
  50. data/test/variant_test.rb +66 -0
  51. metadata +118 -0
@@ -0,0 +1,443 @@
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
+ # FIXME: shouldn't a more special exception be raised here?
46
+ # yes, idea for a good name ? :)
47
+ raise Exception, "Incorrect endianness"
48
+ end
49
+ @idx = 0
50
+ end
51
+
52
+ # Unmarshall the buffer for a given _signature_ and length _len_.
53
+ # Return an array of unmarshalled objects
54
+ def unmarshall(signature, len = nil)
55
+ if len != nil
56
+ if @buffy.size < @idx + len
57
+ raise IncompleteBufferException
58
+ end
59
+ end
60
+ sigtree = Type::Parser.new(signature).parse
61
+ ret = Array.new
62
+ sigtree.each do |elem|
63
+ ret << do_parse(elem)
64
+ end
65
+ ret
66
+ end
67
+
68
+ # Align the pointer index on a byte index of _a_, where a
69
+ # must be 1, 2, 4 or 8.
70
+ def align(a)
71
+ case a
72
+ when 1
73
+ when 2, 4, 8
74
+ bits = a - 1
75
+ @idx = @idx + bits & ~bits
76
+ raise IncompleteBufferException if @idx > @buffy.size
77
+ else
78
+ raise "Unsupported alignment #{a}"
79
+ end
80
+ end
81
+
82
+ ###############################################################
83
+ # FIXME: does anyone except the object itself call the above methods?
84
+ # Yes : Message marshalling code needs to align "body" to 8 byte boundary
85
+ private
86
+
87
+ # Retrieve the next _nbytes_ number of bytes from the buffer.
88
+ def get(nbytes)
89
+ raise IncompleteBufferException if @idx + nbytes > @buffy.size
90
+ ret = @buffy.slice(@idx, nbytes)
91
+ @idx += nbytes
92
+ ret
93
+ end
94
+
95
+ # Retrieve the series of bytes until the next NULL (\0) byte.
96
+ def get_nul_terminated
97
+ raise IncompleteBufferException if not @buffy[@idx..-1] =~ /^([^\0]*)\0/
98
+ str = $1
99
+ raise IncompleteBufferException if @idx + str.size + 1 > @buffy.size
100
+ @idx += str.size + 1
101
+ str
102
+ end
103
+
104
+ # Get the string length and string itself from the buffer.
105
+ # Return the string.
106
+ def get_string
107
+ align(4)
108
+ str_sz = get(4).unpack(@uint32)[0]
109
+ ret = @buffy.slice(@idx, str_sz)
110
+ raise IncompleteBufferException if @idx + str_sz + 1 > @buffy.size
111
+ @idx += str_sz
112
+ if @buffy[@idx].ord != 0
113
+ raise InvalidPacketException, "String is not nul-terminated"
114
+ end
115
+ @idx += 1
116
+ # no exception, see check above
117
+ ret
118
+ end
119
+
120
+ # Get the signature length and signature itself from the buffer.
121
+ # Return the signature.
122
+ def get_signature
123
+ str_sz = get(1).unpack('C')[0]
124
+ ret = @buffy.slice(@idx, str_sz)
125
+ raise IncompleteBufferException if @idx + str_sz + 1 >= @buffy.size
126
+ @idx += str_sz
127
+ if @buffy[@idx].ord != 0
128
+ raise InvalidPacketException, "Type is not nul-terminated"
129
+ end
130
+ @idx += 1
131
+ # no exception, see check above
132
+ ret
133
+ end
134
+
135
+ # Based on the _signature_ type, retrieve a packet from the buffer
136
+ # and return it.
137
+ def do_parse(signature)
138
+ packet = nil
139
+ case signature.sigtype
140
+ when Type::BYTE
141
+ packet = get(1).unpack("C")[0]
142
+ when Type::UINT16
143
+ align(2)
144
+ packet = get(2).unpack(@uint16)[0]
145
+ when Type::INT16
146
+ align(4)
147
+ packet = get(4).unpack(@uint16)[0]
148
+ if (packet & 0x8000) != 0
149
+ packet -= 0x10000
150
+ end
151
+ when Type::UINT32
152
+ align(4)
153
+ packet = get(4).unpack(@uint32)[0]
154
+ when Type::INT32
155
+ align(4)
156
+ packet = get(4).unpack(@uint32)[0]
157
+ if (packet & 0x80000000) != 0
158
+ packet -= 0x100000000
159
+ end
160
+ when Type::UINT64
161
+ align(8)
162
+ packet_l = get(4).unpack(@uint32)[0]
163
+ packet_h = get(4).unpack(@uint32)[0]
164
+ if @endianness == LIL_END
165
+ packet = packet_l + packet_h * 2**32
166
+ else
167
+ packet = packet_l * 2**32 + packet_h
168
+ end
169
+ when Type::INT64
170
+ align(8)
171
+ packet_l = get(4).unpack(@uint32)[0]
172
+ packet_h = get(4).unpack(@uint32)[0]
173
+ if @endianness == LIL_END
174
+ packet = packet_l + packet_h * 2**32
175
+ else
176
+ packet = packet_l * 2**32 + packet_h
177
+ end
178
+ if (packet & 0x8000000000000000) != 0
179
+ packet -= 0x10000000000000000
180
+ end
181
+ when Type::DOUBLE
182
+ align(8)
183
+ packet = get(8).unpack(@double)[0]
184
+ when Type::BOOLEAN
185
+ align(4)
186
+ v = get(4).unpack(@uint32)[0]
187
+ raise InvalidPacketException if not [0, 1].member?(v)
188
+ packet = (v == 1)
189
+ when Type::ARRAY
190
+ align(4)
191
+ # checks please
192
+ array_sz = get(4).unpack(@uint32)[0]
193
+ raise InvalidPacketException if array_sz > 67108864
194
+
195
+ align(signature.child.alignment)
196
+ raise IncompleteBufferException if @idx + array_sz > @buffy.size
197
+
198
+ packet = Array.new
199
+ start_idx = @idx
200
+ while @idx - start_idx < array_sz
201
+ packet << do_parse(signature.child)
202
+ end
203
+
204
+ if signature.child.sigtype == Type::DICT_ENTRY then
205
+ packet = packet.inject(Hash.new) do |hash, pair|
206
+ hash[pair[0]] = pair[1]
207
+ hash
208
+ end
209
+ end
210
+ when Type::STRUCT
211
+ align(8)
212
+ packet = Array.new
213
+ signature.members.each do |elem|
214
+ packet << do_parse(elem)
215
+ end
216
+ when Type::VARIANT
217
+ string = get_signature
218
+ # error checking please
219
+ sig = Type::Parser.new(string).parse[0]
220
+ align(sig.alignment)
221
+ packet = do_parse(sig)
222
+ when Type::OBJECT_PATH
223
+ packet = get_string
224
+ when Type::STRING
225
+ packet = get_string
226
+ when Type::SIGNATURE
227
+ packet = get_signature
228
+ when Type::DICT_ENTRY
229
+ align(8)
230
+ key = do_parse(signature.members[0])
231
+ value = do_parse(signature.members[1])
232
+ packet = [key, value]
233
+ else
234
+ raise NotImplementedError,
235
+ "sigtype: #{signature.sigtype} (#{signature.sigtype.chr})"
236
+ end
237
+ packet
238
+ end # def do_parse
239
+ end # class PacketUnmarshaller
240
+
241
+ # D-Bus packet marshaller class
242
+ #
243
+ # Class that handles the conversion (unmarshalling) of Ruby objects to
244
+ # (binary) payload data.
245
+ class PacketMarshaller
246
+ # The current or result packet.
247
+ # FIXME: allow access only when marshalling is finished
248
+ attr_reader :packet
249
+
250
+ # Create a new marshaller, setting the current packet to the
251
+ # empty packet.
252
+ def initialize(offset = 0)
253
+ @packet = ""
254
+ @offset = offset # for correct alignment of nested marshallers
255
+ end
256
+
257
+ # Round _n_ up to the specified power of two, _a_
258
+ def num_align(n, a)
259
+ case a
260
+ when 1, 2, 4, 8
261
+ bits = a - 1
262
+ n + bits & ~bits
263
+ else
264
+ raise "Unsupported alignment"
265
+ end
266
+ end
267
+
268
+ # Align the buffer with NULL (\0) bytes on a byte length of _a_.
269
+ def align(a)
270
+ @packet = @packet.ljust(num_align(@offset + @packet.length, a) - @offset, 0.chr)
271
+ end
272
+
273
+ # Append the the string _str_ itself to the packet.
274
+ def append_string(str)
275
+ align(4)
276
+ @packet += [str.length].pack("L") + str + "\0"
277
+ end
278
+
279
+ # Append the the signature _signature_ itself to the packet.
280
+ def append_signature(str)
281
+ @packet += str.length.chr + str + "\0"
282
+ end
283
+
284
+ # Append the array type _type_ to the packet and allow for appending
285
+ # the child elements.
286
+ def array(type)
287
+ # Thanks to Peter Rullmann for this line
288
+ align(4)
289
+ sizeidx = @packet.size
290
+ @packet += "ABCD"
291
+ align(type.alignment)
292
+ contentidx = @packet.size
293
+ yield
294
+ sz = @packet.size - contentidx
295
+ raise InvalidPacketException if sz > 67108864
296
+ @packet[sizeidx...sizeidx + 4] = [sz].pack("L")
297
+ end
298
+
299
+ # Align and allow for appending struct fields.
300
+ def struct
301
+ align(8)
302
+ yield
303
+ end
304
+
305
+ # Append a string of bytes without type.
306
+ def append_simple_string(s)
307
+ @packet += s + "\0"
308
+ end
309
+
310
+ # Append a value _val_ to the packet based on its _type_.
311
+ def append(type, val)
312
+ raise TypeException, "Cannot send nil" if val.nil?
313
+
314
+ type = type.chr if type.kind_of?(Fixnum)
315
+ type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
316
+ case type.sigtype
317
+ when Type::BYTE
318
+ @packet += val.chr
319
+ when Type::UINT32
320
+ align(4)
321
+ @packet += [val].pack("L")
322
+ when Type::UINT64
323
+ align(8)
324
+ @packet += [val].pack("Q")
325
+ when Type::INT64
326
+ align(8)
327
+ @packet += [val].pack("q")
328
+ when Type::INT32
329
+ align(4)
330
+ @packet += [val].pack("l")
331
+ when Type::UINT16
332
+ align(2)
333
+ @packet += [val].pack("S")
334
+ when Type::INT16
335
+ align(2)
336
+ @packet += [val].pack("s")
337
+ when Type::DOUBLE
338
+ align(8)
339
+ @packet += [val].pack("d")
340
+ when Type::BOOLEAN
341
+ align(4)
342
+ if val
343
+ @packet += [1].pack("L")
344
+ else
345
+ @packet += [0].pack("L")
346
+ end
347
+ when Type::OBJECT_PATH
348
+ append_string(val)
349
+ when Type::STRING
350
+ append_string(val)
351
+ when Type::SIGNATURE
352
+ append_signature(val)
353
+ when Type::VARIANT
354
+ vartype = nil
355
+ if val.is_a?(Array) and val.size == 2
356
+ if val[0].is_a?(DBus::Type::Type)
357
+ vartype, vardata = val
358
+ elsif val[0].is_a?(String)
359
+ begin
360
+ parsed = Type::Parser.new(val[0]).parse
361
+ vartype = parsed[0] if parsed.size == 1
362
+ vardata = val[1]
363
+ rescue Type::SignatureException
364
+ # no assignment
365
+ end
366
+ end
367
+ end
368
+ if vartype.nil?
369
+ vartype, vardata = PacketMarshaller.make_variant(val)
370
+ vartype = Type::Parser.new(vartype).parse[0]
371
+ end
372
+
373
+ append_signature(vartype.to_s)
374
+ align(vartype.alignment)
375
+ sub = PacketMarshaller.new(@offset + @packet.length)
376
+ sub.append(vartype, vardata)
377
+ @packet += sub.packet
378
+ when Type::ARRAY
379
+ if val.kind_of?(Hash)
380
+ raise TypeException, "Expected an Array but got a Hash" if type.child.sigtype != Type::DICT_ENTRY
381
+ # Damn ruby rocks here
382
+ val = val.to_a
383
+ end
384
+ if not val.kind_of?(Array)
385
+ raise TypeException, "Expected an Array but got a #{val.class}"
386
+ end
387
+ array(type.child) do
388
+ val.each do |elem|
389
+ append(type.child, elem)
390
+ end
391
+ end
392
+ when Type::STRUCT, Type::DICT_ENTRY
393
+ # TODO use duck typing, val.respond_to?
394
+ raise TypeException, "Struct/DE expects an Array" if not val.kind_of?(Array)
395
+ if type.sigtype == Type::DICT_ENTRY and val.size != 2
396
+ raise TypeException, "Dict entry expects a pair"
397
+ end
398
+ if type.members.size != val.size
399
+ raise TypeException, "Struct/DE has #{val.size} elements but type info for #{type.members.size}"
400
+ end
401
+ struct do
402
+ type.members.zip(val).each do |t, v|
403
+ append(t, v)
404
+ end
405
+ end
406
+ else
407
+ raise NotImplementedError,
408
+ "sigtype: #{type.sigtype} (#{type.sigtype.chr})"
409
+ end
410
+ end # def append
411
+
412
+ # Make a [signature, value] pair for a variant
413
+ def self.make_variant(value)
414
+ # TODO: mix in _make_variant to String, Integer...
415
+ if value == true
416
+ ["b", true]
417
+ elsif value == false
418
+ ["b", false]
419
+ elsif value.nil?
420
+ ["b", nil]
421
+ elsif value.is_a? Float
422
+ ["d", value]
423
+ elsif value.is_a? Symbol
424
+ ["s", value.to_s]
425
+ elsif value.is_a? Array
426
+ ["av", value.map {|i| make_variant(i) }]
427
+ elsif value.is_a? Hash
428
+ h = {}
429
+ value.each_key {|k| h[k] = make_variant(value[k]) }
430
+ ["a{sv}", h]
431
+ elsif value.respond_to? :to_str
432
+ ["s", value.to_str]
433
+ elsif value.respond_to? :to_int
434
+ i = value.to_int
435
+ if -2_147_483_648 <= i && i < 2_147_483_648
436
+ ["i", i]
437
+ else
438
+ ["x", i]
439
+ end
440
+ end
441
+ end
442
+ end # class PacketMarshaller
443
+ end # module DBus
@@ -0,0 +1,100 @@
1
+ # This file is part of the ruby-dbus project
2
+ # Copyright (C) 2007 Arnaud Cornet and Paul van Tilburg
3
+ #
4
+ # This library is free software; you can redistribute it and/or
5
+ # modify it under the terms of the GNU Lesser General Public
6
+ # License, version 2.1 as published by the Free Software Foundation.
7
+ # See the file "COPYING" for the exact licensing terms.
8
+
9
+ module DBus
10
+ # Exception raised when an erroneous match rule type is encountered.
11
+ class MatchRuleException < Exception
12
+ end
13
+
14
+ # = D-Bus match rule class
15
+ #
16
+ # FIXME
17
+ class MatchRule
18
+ # The list of possible match filters.
19
+ FILTERS = [:sender, :interface, :member, :path, :destination, :type]
20
+ # The sender filter.
21
+ attr_accessor :sender
22
+ # The interface filter.
23
+ attr_accessor :interface
24
+ # The member filter.
25
+ attr_accessor :member
26
+ # The path filter.
27
+ attr_accessor :path
28
+ # The destination filter.
29
+ attr_accessor :destination
30
+ # The type type that is matched.
31
+ attr_reader :type
32
+
33
+ # Create a new match rule
34
+ def initialize
35
+ @sender = @interface = @member = @path = @destination = @type = nil
36
+ end
37
+
38
+ # Set the message types to filter to type _t_.
39
+ # Possible message types are: signal, method_call, method_return, and error.
40
+ def type=(t)
41
+ if not ['signal', 'method_call', 'method_return', 'error'].member?(t)
42
+ raise MatchRuleException, t
43
+ end
44
+ @type = t
45
+ end
46
+
47
+ # Returns a match rule string version of the object.
48
+ # E.g.: "type='signal',sender='org.freedesktop.DBus',interface='org.freedesktop.DBus',member='Foo',path='/bar/foo',destination=':452345.34',arg2='bar'"
49
+ def to_s
50
+ FILTERS.select do |sym|
51
+ not method(sym).call.nil?
52
+ end.collect do |sym|
53
+ "#{sym.to_s}='#{method(sym).call}'"
54
+ end.join(",")
55
+ end
56
+
57
+ # Parses a match rule string _s_ and sets the filters on the object.
58
+ def from_s(str)
59
+ str.split(",").each do |eq|
60
+ if eq =~ /^(.*)='([^']*)'$/
61
+ # "
62
+ name = $1
63
+ val = $2
64
+ if FILTERS.member?(name.to_sym)
65
+ method(name + "=").call(val)
66
+ else
67
+ raise MatchRuleException, name
68
+ end
69
+ end
70
+ end
71
+ self
72
+ end
73
+
74
+ # Sets the match rule to filter for the given _signal_ and the
75
+ # given interface _intf_.
76
+ def from_signal(intf, signal)
77
+ signal = signal.name unless signal.is_a?(String)
78
+ self.type = "signal"
79
+ self.interface = intf.name
80
+ self.member = signal
81
+ self.path = intf.object.path
82
+ self
83
+ end
84
+
85
+ # Determines whether a message _msg_ matches the match rule.
86
+ def match(msg)
87
+ if @type
88
+ if {Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
89
+ Message::METHOD_RETURN => "method_return",
90
+ Message::ERROR => "error"}[msg.message_type] != @type
91
+ return false
92
+ end
93
+ end
94
+ return false if @interface and @interface != msg.interface
95
+ return false if @member and @member != msg.member
96
+ return false if @path and @path != msg.path
97
+ true
98
+ end
99
+ end # class MatchRule
100
+ end # module D-Bus