pangdudu-ruby-dbus 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +504 -0
- data/README.rdoc +111 -0
- data/config/remote.session.dbus.conf +39 -0
- data/config/start_dbus_session.sh +2 -0
- data/lib/dbus/auth.rb +238 -0
- data/lib/dbus/bus.rb +719 -0
- data/lib/dbus/export.rb +123 -0
- data/lib/dbus/introspect.rb +509 -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/lib/dbus.rb +88 -0
- data/test/simple_socket_test.rb +27 -0
- metadata +78 -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
|