demirten-ruby-dbus 0.2.4.6
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.
- data/COPYING +504 -0
- data/README.rdoc +127 -0
- data/config/remote.session.dbus.conf +39 -0
- data/config/start_dbus_session.sh +2 -0
- data/lib/dbus.rb +86 -0
- data/lib/dbus/auth.rb +251 -0
- data/lib/dbus/bus.rb +752 -0
- data/lib/dbus/export.rb +123 -0
- data/lib/dbus/introspect.rb +572 -0
- data/lib/dbus/marshall.rb +396 -0
- data/lib/dbus/matchrule.rb +98 -0
- data/lib/dbus/message.rb +281 -0
- data/lib/dbus/type.rb +207 -0
- data/test/simple_socket_test.rb +27 -0
- metadata +88 -0
@@ -0,0 +1,396 @@
|
|
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] != 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] != 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
|
+
type = type.chr if type.kind_of?(Fixnum)
|
313
|
+
type = Type::Parser.new(type).parse[0] if type.kind_of?(String)
|
314
|
+
case type.sigtype
|
315
|
+
when Type::BYTE
|
316
|
+
@packet += val.chr
|
317
|
+
when Type::UINT32
|
318
|
+
align(4)
|
319
|
+
@packet += [val].pack("L")
|
320
|
+
when Type::UINT64
|
321
|
+
align(8)
|
322
|
+
@packet += [val].pack("Q")
|
323
|
+
when Type::INT64
|
324
|
+
align(8)
|
325
|
+
@packet += [val].pack("q")
|
326
|
+
when Type::INT32
|
327
|
+
align(4)
|
328
|
+
@packet += [val].pack("l")
|
329
|
+
when Type::UINT16
|
330
|
+
align(2)
|
331
|
+
@packet += [val].pack("S")
|
332
|
+
when Type::INT16
|
333
|
+
align(2)
|
334
|
+
@packet += [val].pack("s")
|
335
|
+
when Type::DOUBLE
|
336
|
+
align(8)
|
337
|
+
@packet += [val].pack("d")
|
338
|
+
when Type::BOOLEAN
|
339
|
+
align(4)
|
340
|
+
if val
|
341
|
+
@packet += [1].pack("L")
|
342
|
+
else
|
343
|
+
@packet += [0].pack("L")
|
344
|
+
end
|
345
|
+
when Type::OBJECT_PATH
|
346
|
+
append_string(val)
|
347
|
+
when Type::STRING
|
348
|
+
append_string(val)
|
349
|
+
when Type::SIGNATURE
|
350
|
+
append_signature(val)
|
351
|
+
when Type::VARIANT
|
352
|
+
if not val.kind_of?(Array)
|
353
|
+
raise TypeException
|
354
|
+
end
|
355
|
+
vartype, vardata = val
|
356
|
+
vartype = Type::Parser.new(vartype).parse[0] if vartype.kind_of?(String)
|
357
|
+
append_signature(vartype.to_s)
|
358
|
+
align(vartype.alignment)
|
359
|
+
sub = PacketMarshaller.new(@offset + @packet.length)
|
360
|
+
sub.append(vartype, vardata)
|
361
|
+
@packet += sub.packet
|
362
|
+
when Type::ARRAY
|
363
|
+
if val.kind_of?(Hash)
|
364
|
+
raise TypeException if type.child.sigtype != Type::DICT_ENTRY
|
365
|
+
# Damn ruby rocks here
|
366
|
+
val = val.to_a
|
367
|
+
end
|
368
|
+
if not val.kind_of?(Array)
|
369
|
+
raise TypeException
|
370
|
+
end
|
371
|
+
array(type.child) do
|
372
|
+
val.each do |elem|
|
373
|
+
append(type.child, elem)
|
374
|
+
end
|
375
|
+
end
|
376
|
+
when Type::STRUCT, Type::DICT_ENTRY
|
377
|
+
raise TypeException if not val.kind_of?(Array)
|
378
|
+
if type.sigtype == Type::DICT_ENTRY and val.size != 2
|
379
|
+
raise TypeException
|
380
|
+
end
|
381
|
+
struct do
|
382
|
+
idx = 0
|
383
|
+
while val[idx] != nil
|
384
|
+
type.members.each do |subtype|
|
385
|
+
raise TypeException if val[idx] == nil
|
386
|
+
append(subtype, val[idx])
|
387
|
+
idx += 1
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
else
|
392
|
+
raise NotImplementedError
|
393
|
+
end
|
394
|
+
end # def append
|
395
|
+
end # class PacketMarshaller
|
396
|
+
end # module DBus
|
@@ -0,0 +1,98 @@
|
|
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
|
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
|
+
s.split(",").each do |eq|
|
60
|
+
if eq =~ /^(.*)='([^']*)'$/
|
61
|
+
name = $1
|
62
|
+
val = $1
|
63
|
+
if FILTERS.member?(name.to_sym)
|
64
|
+
method(name + "=").call(val)
|
65
|
+
else
|
66
|
+
raise MatchRuleException
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Sets the match rule to filter for the given _signal_ and the
|
73
|
+
# given interface _intf_.
|
74
|
+
def from_signal(intf, signal)
|
75
|
+
signal = signal.name unless signal.is_a?(String)
|
76
|
+
self.type = "signal"
|
77
|
+
self.interface = intf.name
|
78
|
+
self.member = signal
|
79
|
+
self.path = intf.object.path
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
# Determines whether a message _msg_ matches the match rule.
|
84
|
+
def match(msg)
|
85
|
+
if @type
|
86
|
+
if {Message::SIGNAL => "signal", Message::METHOD_CALL => "method_call",
|
87
|
+
Message::METHOD_RETURN => "method_return",
|
88
|
+
Message::ERROR => "error"}[msg.message_type] != @type
|
89
|
+
return false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
return false if @interface and @interface != msg.interface
|
93
|
+
return false if @member and @member != msg.member
|
94
|
+
return false if @path and @path != msg.path
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end # class MatchRule
|
98
|
+
end # module D-Bus
|