pangdudu-ruby-dbus 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. data/lib/dbus/bus.rb +42 -498
  2. data/lib/dbus/connection.rb +457 -0
  3. metadata +2 -1
data/lib/dbus/bus.rb CHANGED
@@ -11,11 +11,53 @@
11
11
  require 'socket'
12
12
  require 'thread'
13
13
  require 'singleton'
14
+ require 'connection' #outsourced connection module
14
15
 
15
16
  # = D-Bus main module
16
17
  #
17
18
  # Module containing all the D-Bus modules and classes.
18
19
  module DBus
20
+
21
+ # = D-Bus session bus class
22
+ #
23
+ # The session bus is a session specific bus (mostly for desktop use).
24
+ # This is a singleton class.
25
+ class SessionBus < Connection
26
+ include Singleton
27
+
28
+ # Get the the default session bus.
29
+ def initialize
30
+ super(ENV["DBUS_SESSION_BUS_ADDRESS"])
31
+ connect
32
+ send_hello
33
+ end
34
+ end
35
+
36
+ # = D-Bus system bus class
37
+ #
38
+ # The system bus is a system-wide bus mostly used for global or
39
+ # system usages. This is a singleton class.
40
+ class SystemBus < Connection
41
+ include Singleton
42
+
43
+ # Get the default system bus.
44
+ def initialize
45
+ super(SystemSocketName)
46
+ connect
47
+ send_hello
48
+ end
49
+ end
50
+
51
+ # FIXME: we should get rid of these
52
+
53
+ def DBus.system_bus
54
+ SystemBus.instance
55
+ end
56
+
57
+ def DBus.session_bus
58
+ SessionBus.instance
59
+ end
60
+
19
61
  # This represents a remote service. It should not be instancied directly
20
62
  # Use Bus::service()
21
63
  class Service
@@ -159,504 +201,6 @@ module DBus
159
201
  end
160
202
  end # class Inspect
161
203
 
162
- # FIXME: rename Connection to Bus?
163
-
164
- # D-Bus main connection class
165
- #
166
- # Main class that maintains a connection to a bus and can handle incoming
167
- # and outgoing messages.
168
- class Connection
169
- # The unique name (by specification) of the message.
170
- attr_reader :unique_name
171
- # The socket that is used to connect with the bus.
172
- attr_reader :socket
173
-
174
- # Create a new connection to the bus for a given connect _path_. _path_
175
- # format is described in the D-Bus specification:
176
- # http://dbus.freedesktop.org/doc/dbus-specification.html#addresses
177
- # and is something like:
178
- # "transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2"
179
- # e.g. "unix:path=/tmp/dbus-test"
180
- #
181
- # Current implementation of ruby-dbus supports only a single server
182
- # address and only "unix:path=...,guid=..." and
183
- # "unix:abstract=...,guid=..." forms
184
- def initialize(path)
185
- @path = path
186
- @unique_name = nil
187
- @buffer = ""
188
- @method_call_replies = Hash.new
189
- @method_call_msgs = Hash.new
190
- @signal_matchrules = Array.new
191
- @proxy = nil
192
- # FIXME: can be TCP or any stream
193
- @socket = Socket.new(Socket::Constants::PF_UNIX,
194
- Socket::Constants::SOCK_STREAM, 0)
195
- @object_root = Node.new("/")
196
- end
197
-
198
- # Connect to the bus and initialize the connection.
199
- def connect
200
- parse_session_string
201
- if @transport == "unix" and @type == "abstract"
202
- if HOST_END == LIL_END
203
- sockaddr = "\1\0\0#{@unix_abstract}"
204
- else
205
- sockaddr = "\0\1\0#{@unix_abstract}"
206
- end
207
- elsif @transport == "unix" and @type == "path"
208
- sockaddr = Socket.pack_sockaddr_un(@unix)
209
- end
210
- @socket.connect(sockaddr)
211
- init_connection
212
- end
213
-
214
- # Send the buffer _buf_ to the bus using Connection#writel.
215
- def send(buf)
216
- @socket.write(buf)
217
- end
218
-
219
- # Tell a bus to register itself on the glib main loop
220
- def glibize
221
- require 'glib2'
222
- # Circumvent a ruby-glib bug
223
- @channels ||= Array.new
224
-
225
- gio = GLib::IOChannel.new(@socket.fileno)
226
- @channels << gio
227
- gio.add_watch(GLib::IOChannel::IN) do |c, ch|
228
- update_buffer
229
- messages.each do |msg|
230
- process(msg)
231
- end
232
- true
233
- end
234
- end
235
-
236
- # FIXME: describe the following names, flags and constants.
237
- # See DBus spec for definition
238
- NAME_FLAG_ALLOW_REPLACEMENT = 0x1
239
- NAME_FLAG_REPLACE_EXISTING = 0x2
240
- NAME_FLAG_DO_NOT_QUEUE = 0x4
241
-
242
- REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x1
243
- REQUEST_NAME_REPLY_IN_QUEUE = 0x2
244
- REQUEST_NAME_REPLY_EXISTS = 0x3
245
- REQUEST_NAME_REPLY_ALREADY_OWNER = 0x4
246
-
247
- DBUSXMLINTRO = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
248
- "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
249
- <node>
250
- <interface name="org.freedesktop.DBus.Introspectable">
251
- <method name="Introspect">
252
- <arg name="data" direction="out" type="s"/>
253
- </method>
254
- </interface>
255
- <interface name="org.freedesktop.DBus">
256
- <method name="RequestName">
257
- <arg direction="in" type="s"/>
258
- <arg direction="in" type="u"/>
259
- <arg direction="out" type="u"/>
260
- </method>
261
- <method name="ReleaseName">
262
- <arg direction="in" type="s"/>
263
- <arg direction="out" type="u"/>
264
- </method>
265
- <method name="StartServiceByName">
266
- <arg direction="in" type="s"/>
267
- <arg direction="in" type="u"/>
268
- <arg direction="out" type="u"/>
269
- </method>
270
- <method name="Hello">
271
- <arg direction="out" type="s"/>
272
- </method>
273
- <method name="NameHasOwner">
274
- <arg direction="in" type="s"/>
275
- <arg direction="out" type="b"/>
276
- </method>
277
- <method name="ListNames">
278
- <arg direction="out" type="as"/>
279
- </method>
280
- <method name="ListActivatableNames">
281
- <arg direction="out" type="as"/>
282
- </method>
283
- <method name="AddMatch">
284
- <arg direction="in" type="s"/>
285
- </method>
286
- <method name="RemoveMatch">
287
- <arg direction="in" type="s"/>
288
- </method>
289
- <method name="GetNameOwner">
290
- <arg direction="in" type="s"/>
291
- <arg direction="out" type="s"/>
292
- </method>
293
- <method name="ListQueuedOwners">
294
- <arg direction="in" type="s"/>
295
- <arg direction="out" type="as"/>
296
- </method>
297
- <method name="GetConnectionUnixUser">
298
- <arg direction="in" type="s"/>
299
- <arg direction="out" type="u"/>
300
- </method>
301
- <method name="GetConnectionUnixProcessID">
302
- <arg direction="in" type="s"/>
303
- <arg direction="out" type="u"/>
304
- </method>
305
- <method name="GetConnectionSELinuxSecurityContext">
306
- <arg direction="in" type="s"/>
307
- <arg direction="out" type="ay"/>
308
- </method>
309
- <method name="ReloadConfig">
310
- </method>
311
- <signal name="NameOwnerChanged">
312
- <arg type="s"/>
313
- <arg type="s"/>
314
- <arg type="s"/>
315
- </signal>
316
- <signal name="NameLost">
317
- <arg type="s"/>
318
- </signal>
319
- <signal name="NameAcquired">
320
- <arg type="s"/>
321
- </signal>
322
- </interface>
323
- </node>
324
- '
325
-
326
- def introspect_data(dest, path)
327
- m = DBus::Message.new(DBus::Message::METHOD_CALL)
328
- m.path = path
329
- m.interface = "org.freedesktop.DBus.Introspectable"
330
- m.destination = dest
331
- m.member = "Introspect"
332
- m.sender = unique_name
333
- if not block_given?
334
- # introspect in synchronous !
335
- send_sync(m) do |rmsg|
336
- if rmsg.is_a?(Error)
337
- raise rmsg
338
- else
339
- return rmsg.params[0]
340
- end
341
- end
342
- else
343
- send(m.marshall)
344
- on_return(m) do |rmsg|
345
- if rmsg.is_a?(Error)
346
- yield rmsg
347
- else
348
- yield rmsg.params[0]
349
- end
350
- end
351
- end
352
- nil
353
- end
354
-
355
- # Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
356
- # _dest_ is the service and _path_ the object path you want to introspect
357
- # If a code block is given, the introspect call in asynchronous. If not
358
- # data is returned
359
- #
360
- # FIXME: link to ProxyObject data definition
361
- # The returned object is a ProxyObject that has methods you can call to
362
- # issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN
363
- def introspect(dest, path)
364
- if not block_given?
365
- # introspect in synchronous !
366
- data = introspect_data(dest, path)
367
- pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
368
- return pof.build
369
- else
370
- introspect_data(dest, path) do |data|
371
- yield(DBus::ProxyObjectFactory.new(data, self, dest, path).build)
372
- end
373
- end
374
- end
375
-
376
- # Exception raised when a service name is requested that is not available.
377
- class NameRequestError < Exception
378
- end
379
-
380
- # Attempt to request a service _name_.
381
- def request_service(name)
382
- r = proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING)
383
- raise NameRequestError if r[0] != REQUEST_NAME_REPLY_PRIMARY_OWNER
384
- @service = Service.new(name, self)
385
- @service
386
- end
387
-
388
- # Set up a ProxyObject for the bus itself, since the bus is introspectable.
389
- # Returns the object.
390
- def proxy
391
- if @proxy == nil
392
- path = "/org/freedesktop/DBus"
393
- dest = "org.freedesktop.DBus"
394
- pof = DBus::ProxyObjectFactory.new(DBUSXMLINTRO, self, dest, path)
395
- @proxy = pof.build["org.freedesktop.DBus"]
396
- end
397
- @proxy
398
- end
399
-
400
- # Fill (append) the buffer from data that might be available on the
401
- # socket.
402
- def update_buffer
403
- @buffer += @socket.read_nonblock(MSG_BUF_SIZE)
404
- end
405
-
406
- # Get one message from the bus and remove it from the buffer.
407
- # Return the message.
408
- def pop_message
409
- ret = nil
410
- begin
411
- ret, size = Message.new.unmarshall_buffer(@buffer)
412
- @buffer.slice!(0, size)
413
- rescue IncompleteBufferException => e
414
- # fall through, let ret be null
415
- end
416
- ret
417
- end
418
-
419
- # Retrieve all the messages that are currently in the buffer.
420
- def messages
421
- ret = Array.new
422
- while msg = pop_message
423
- ret << msg
424
- end
425
- ret
426
- end
427
-
428
- # The buffer size for messages.
429
- MSG_BUF_SIZE = 4096
430
-
431
- # Update the buffer and retrieve all messages using Connection#messages.
432
- # Return the messages.
433
- def poll_messages
434
- ret = nil
435
- r, d, d = IO.select([@socket], nil, nil, 0)
436
- if r and r.size > 0
437
- update_buffer
438
- end
439
- messages
440
- end
441
-
442
- # Wait for a message to arrive. Return it once it is available.
443
- def wait_for_message
444
- ret = pop_message
445
- while ret == nil
446
- r, d, d = IO.select([@socket])
447
- if r and r[0] == @socket
448
- update_buffer
449
- ret = pop_message
450
- end
451
- end
452
- ret
453
- end
454
-
455
- # Send a message _m_ on to the bus. This is done synchronously, thus
456
- # the call will block until a reply message arrives.
457
- def send_sync(m, &retc) # :yields: reply/return message
458
- send(m.marshall)
459
- @method_call_msgs[m.serial] = m
460
- @method_call_replies[m.serial] = retc
461
-
462
- retm = wait_for_message
463
- process(retm)
464
- until [DBus::Message::ERROR,
465
- DBus::Message::METHOD_RETURN].include?(retm.message_type) and
466
- retm.reply_serial == m.serial
467
- retm = wait_for_message
468
- process(retm)
469
- end
470
- end
471
-
472
- # Specify a code block that has to be executed when a reply for
473
- # message _m_ is received.
474
- def on_return(m, &retc)
475
- # Have a better exception here
476
- if m.message_type != Message::METHOD_CALL
477
- raise "on_return should only get method_calls"
478
- end
479
- @method_call_msgs[m.serial] = m
480
- @method_call_replies[m.serial] = retc
481
- end
482
-
483
- # Asks bus to send us messages matching mr, and execute slot when
484
- # received
485
- def add_match(mr, &slot)
486
- # check this is a signal.
487
- @signal_matchrules << [mr, slot]
488
- self.proxy.AddMatch(mr.to_s)
489
- end
490
-
491
- # Process a message _m_ based on its type.
492
- # method call:: FIXME...
493
- # method call return value:: FIXME...
494
- # signal:: FIXME...
495
- # error:: FIXME...
496
- def process(m)
497
- case m.message_type
498
- when Message::ERROR, Message::METHOD_RETURN
499
- raise InvalidPacketException if m.reply_serial == nil
500
- mcs = @method_call_replies[m.reply_serial]
501
- if not mcs
502
- puts "no return code for #{mcs.inspect} (#{m.inspect})" if $DEBUG
503
- else
504
- if m.message_type == Message::ERROR
505
- mcs.call(Error.new(m))
506
- else
507
- mcs.call(m)
508
- end
509
- @method_call_replies.delete(m.reply_serial)
510
- @method_call_msgs.delete(m.reply_serial)
511
- end
512
- when DBus::Message::METHOD_CALL
513
- if m.path == "/org/freedesktop/DBus"
514
- puts "Got method call on /org/freedesktop/DBus" if $DEBUG
515
- end
516
- # handle introspectable as an exception:
517
- if m.interface == "org.freedesktop.DBus.Introspectable" and
518
- m.member == "Introspect"
519
- reply = Message.new(Message::METHOD_RETURN).reply_to(m)
520
- reply.sender = @unique_name
521
- node = @service.get_node(m.path)
522
- raise NotImplementedError if not node
523
- reply.sender = @unique_name
524
- reply.add_param(Type::STRING, @service.get_node(m.path).to_xml)
525
- send(reply.marshall)
526
- else
527
- node = @service.get_node(m.path)
528
- return if node.nil?
529
- obj = node.object
530
- return if obj.nil?
531
- obj.dispatch(m) if obj
532
- end
533
- when DBus::Message::SIGNAL
534
- @signal_matchrules.each do |elem|
535
- mr, slot = elem
536
- if mr.match(m)
537
- slot.call(m)
538
- return
539
- end
540
- end
541
- else
542
- puts "Unknown message type: #{m.message_type}" if $DEBUG
543
- end
544
- end
545
-
546
- # Retrieves the service with the given _name_.
547
- def service(name)
548
- # The service might not exist at this time so we cannot really check
549
- # anything
550
- Service.new(name, self)
551
- end
552
- alias :[] :service
553
-
554
- # Emit a signal event for the given _service_, object _obj_, interface
555
- # _intf_ and signal _sig_ with arguments _args_.
556
- def emit(service, obj, intf, sig, *args)
557
- m = Message.new(DBus::Message::SIGNAL)
558
- m.path = obj.path
559
- m.interface = intf.name
560
- m.member = sig.name
561
- m.sender = service.name
562
- i = 0
563
- sig.params.each do |par|
564
- m.add_param(par[1], args[i])
565
- i += 1
566
- end
567
- send(m.marshall)
568
- end
569
-
570
- ###########################################################################
571
- private
572
-
573
- # Send a hello messages to the bus to let it know we are here.
574
- def send_hello
575
- m = Message.new(DBus::Message::METHOD_CALL)
576
- m.path = "/org/freedesktop/DBus"
577
- m.destination = "org.freedesktop.DBus"
578
- m.interface = "org.freedesktop.DBus"
579
- m.member = "Hello"
580
- send_sync(m) do |rmsg|
581
- @unique_name = rmsg.destination
582
- puts "Got hello reply. Our unique_name is #{@unique_name}" if $DEBUG
583
- end
584
- end
585
-
586
- # Parse the session string (socket address).
587
- def parse_session_string
588
- path_parsed = /^([^:]*):([^;]*)$/.match(@path)
589
- @transport = path_parsed[1]
590
- adr = path_parsed[2]
591
- if @transport == "unix"
592
- adr.split(",").each do |eqstr|
593
- idx, val = eqstr.split("=")
594
- case idx
595
- when "path"
596
- @type = idx
597
- @unix = val
598
- when "abstract"
599
- @type = idx
600
- @unix_abstract = val
601
- when "guid"
602
- @guid = val
603
- end
604
- end
605
- end
606
- end
607
-
608
- # Initialize the connection to the bus.
609
- def init_connection
610
- @client = Client.new(@socket)
611
- @client.authenticate
612
- # TODO: code some real stuff here
613
- #writel("AUTH EXTERNAL 31303030")
614
- #s = readl
615
- # parse OK ?
616
- #writel("BEGIN")
617
- end
618
- end # class Connection
619
-
620
- # = D-Bus session bus class
621
- #
622
- # The session bus is a session specific bus (mostly for desktop use).
623
- # This is a singleton class.
624
- class SessionBus < Connection
625
- include Singleton
626
-
627
- # Get the the default session bus.
628
- def initialize
629
- super(ENV["DBUS_SESSION_BUS_ADDRESS"])
630
- connect
631
- send_hello
632
- end
633
- end
634
-
635
- # = D-Bus system bus class
636
- #
637
- # The system bus is a system-wide bus mostly used for global or
638
- # system usages. This is a singleton class.
639
- class SystemBus < Connection
640
- include Singleton
641
-
642
- # Get the default system bus.
643
- def initialize
644
- super(SystemSocketName)
645
- connect
646
- send_hello
647
- end
648
- end
649
-
650
- # FIXME: we should get rid of these
651
-
652
- def DBus.system_bus
653
- SystemBus.instance
654
- end
655
-
656
- def DBus.session_bus
657
- SessionBus.instance
658
- end
659
-
660
204
  # = Main event loop class.
661
205
  #
662
206
  # Class that takes care of handling message and signal events
@@ -0,0 +1,457 @@
1
+ # D-Bus main connection class
2
+ #
3
+ # Main class that maintains a connection to a bus and can handle incoming
4
+ # and outgoing messages.
5
+ class Connection
6
+ # The unique name (by specification) of the message.
7
+ attr_reader :unique_name
8
+ # The socket that is used to connect with the bus.
9
+ attr_reader :socket
10
+
11
+ # Create a new connection to the bus for a given connect _path_. _path_
12
+ # format is described in the D-Bus specification:
13
+ # http://dbus.freedesktop.org/doc/dbus-specification.html#addresses
14
+ # and is something like:
15
+ # "transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2"
16
+ # e.g. "unix:path=/tmp/dbus-test"
17
+ #
18
+ # Current implementation of ruby-dbus supports only a single server
19
+ # address and only "unix:path=...,guid=..." and
20
+ # "unix:abstract=...,guid=..." forms
21
+ def initialize(path)
22
+ @path = path
23
+ @unique_name = nil
24
+ @buffer = ""
25
+ @method_call_replies = Hash.new
26
+ @method_call_msgs = Hash.new
27
+ @signal_matchrules = Array.new
28
+ @proxy = nil
29
+ # FIXME: can be TCP or any stream
30
+ @socket = Socket.new(Socket::Constants::PF_UNIX,
31
+ Socket::Constants::SOCK_STREAM, 0)
32
+ @object_root = Node.new("/")
33
+ end
34
+
35
+ # Connect to the bus and initialize the connection.
36
+ def connect
37
+ parse_session_string
38
+ if @transport == "unix" and @type == "abstract"
39
+ if HOST_END == LIL_END
40
+ sockaddr = "\1\0\0#{@unix_abstract}"
41
+ else
42
+ sockaddr = "\0\1\0#{@unix_abstract}"
43
+ end
44
+ elsif @transport == "unix" and @type == "path"
45
+ sockaddr = Socket.pack_sockaddr_un(@unix)
46
+ end
47
+ @socket.connect(sockaddr)
48
+ init_connection
49
+ end
50
+
51
+ # Send the buffer _buf_ to the bus using Connection#writel.
52
+ def send(buf)
53
+ @socket.write(buf)
54
+ end
55
+
56
+ # Tell a bus to register itself on the glib main loop
57
+ def glibize
58
+ puts "dbus/bus.rb-221: Entered glibize."
59
+ require 'glib2'
60
+ # Circumvent a ruby-glib bug
61
+ @channels ||= Array.new
62
+
63
+ gio = GLib::IOChannel.new(@socket.fileno)
64
+ @channels << gio
65
+ gio.add_watch(GLib::IOChannel::IN) do |c, ch|
66
+ update_buffer
67
+ messages.each do |msg|
68
+ process(msg)
69
+ end
70
+ true
71
+ end
72
+ end
73
+
74
+ # FIXME: describe the following names, flags and constants.
75
+ # See DBus spec for definition
76
+ NAME_FLAG_ALLOW_REPLACEMENT = 0x1
77
+ NAME_FLAG_REPLACE_EXISTING = 0x2
78
+ NAME_FLAG_DO_NOT_QUEUE = 0x4
79
+
80
+ REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x1
81
+ REQUEST_NAME_REPLY_IN_QUEUE = 0x2
82
+ REQUEST_NAME_REPLY_EXISTS = 0x3
83
+ REQUEST_NAME_REPLY_ALREADY_OWNER = 0x4
84
+
85
+ DBUSXMLINTRO = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
86
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
87
+ <node>
88
+ <interface name="org.freedesktop.DBus.Introspectable">
89
+ <method name="Introspect">
90
+ <arg name="data" direction="out" type="s"/>
91
+ </method>
92
+ </interface>
93
+ <interface name="org.freedesktop.DBus">
94
+ <method name="RequestName">
95
+ <arg direction="in" type="s"/>
96
+ <arg direction="in" type="u"/>
97
+ <arg direction="out" type="u"/>
98
+ </method>
99
+ <method name="ReleaseName">
100
+ <arg direction="in" type="s"/>
101
+ <arg direction="out" type="u"/>
102
+ </method>
103
+ <method name="StartServiceByName">
104
+ <arg direction="in" type="s"/>
105
+ <arg direction="in" type="u"/>
106
+ <arg direction="out" type="u"/>
107
+ </method>
108
+ <method name="Hello">
109
+ <arg direction="out" type="s"/>
110
+ </method>
111
+ <method name="NameHasOwner">
112
+ <arg direction="in" type="s"/>
113
+ <arg direction="out" type="b"/>
114
+ </method>
115
+ <method name="ListNames">
116
+ <arg direction="out" type="as"/>
117
+ </method>
118
+ <method name="ListActivatableNames">
119
+ <arg direction="out" type="as"/>
120
+ </method>
121
+ <method name="AddMatch">
122
+ <arg direction="in" type="s"/>
123
+ </method>
124
+ <method name="RemoveMatch">
125
+ <arg direction="in" type="s"/>
126
+ </method>
127
+ <method name="GetNameOwner">
128
+ <arg direction="in" type="s"/>
129
+ <arg direction="out" type="s"/>
130
+ </method>
131
+ <method name="ListQueuedOwners">
132
+ <arg direction="in" type="s"/>
133
+ <arg direction="out" type="as"/>
134
+ </method>
135
+ <method name="GetConnectionUnixUser">
136
+ <arg direction="in" type="s"/>
137
+ <arg direction="out" type="u"/>
138
+ </method>
139
+ <method name="GetConnectionUnixProcessID">
140
+ <arg direction="in" type="s"/>
141
+ <arg direction="out" type="u"/>
142
+ </method>
143
+ <method name="GetConnectionSELinuxSecurityContext">
144
+ <arg direction="in" type="s"/>
145
+ <arg direction="out" type="ay"/>
146
+ </method>
147
+ <method name="ReloadConfig">
148
+ </method>
149
+ <signal name="NameOwnerChanged">
150
+ <arg type="s"/>
151
+ <arg type="s"/>
152
+ <arg type="s"/>
153
+ </signal>
154
+ <signal name="NameLost">
155
+ <arg type="s"/>
156
+ </signal>
157
+ <signal name="NameAcquired">
158
+ <arg type="s"/>
159
+ </signal>
160
+ </interface>
161
+ </node>
162
+ '
163
+
164
+ def introspect_data(dest, path)
165
+ m = DBus::Message.new(DBus::Message::METHOD_CALL)
166
+ m.path = path
167
+ m.interface = "org.freedesktop.DBus.Introspectable"
168
+ m.destination = dest
169
+ m.member = "Introspect"
170
+ m.sender = unique_name
171
+ if not block_given?
172
+ # introspect in synchronous !
173
+ send_sync(m) do |rmsg|
174
+ if rmsg.is_a?(Error)
175
+ raise rmsg
176
+ else
177
+ return rmsg.params[0]
178
+ end
179
+ end
180
+ else
181
+ send(m.marshall)
182
+ on_return(m) do |rmsg|
183
+ if rmsg.is_a?(Error)
184
+ yield rmsg
185
+ else
186
+ yield rmsg.params[0]
187
+ end
188
+ end
189
+ end
190
+ nil
191
+ end
192
+
193
+ # Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
194
+ # _dest_ is the service and _path_ the object path you want to introspect
195
+ # If a code block is given, the introspect call in asynchronous. If not
196
+ # data is returned
197
+ #
198
+ # FIXME: link to ProxyObject data definition
199
+ # The returned object is a ProxyObject that has methods you can call to
200
+ # issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN
201
+ def introspect(dest, path)
202
+ if not block_given?
203
+ # introspect in synchronous !
204
+ data = introspect_data(dest, path)
205
+ pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
206
+ return pof.build
207
+ else
208
+ introspect_data(dest, path) do |data|
209
+ yield(DBus::ProxyObjectFactory.new(data, self, dest, path).build)
210
+ end
211
+ end
212
+ end
213
+
214
+ # Exception raised when a service name is requested that is not available.
215
+ class NameRequestError < Exception
216
+ end
217
+
218
+ # Attempt to request a service _name_.
219
+ def request_service(name)
220
+ r = proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING)
221
+ raise NameRequestError if r[0] != REQUEST_NAME_REPLY_PRIMARY_OWNER
222
+ @service = Service.new(name, self)
223
+ @service
224
+ end
225
+
226
+ # Set up a ProxyObject for the bus itself, since the bus is introspectable.
227
+ # Returns the object.
228
+ def proxy
229
+ if @proxy == nil
230
+ path = "/org/freedesktop/DBus"
231
+ dest = "org.freedesktop.DBus"
232
+ pof = DBus::ProxyObjectFactory.new(DBUSXMLINTRO, self, dest, path)
233
+ @proxy = pof.build["org.freedesktop.DBus"]
234
+ end
235
+ @proxy
236
+ end
237
+
238
+ # Fill (append) the buffer from data that might be available on the
239
+ # socket.
240
+ def update_buffer
241
+ @buffer += @socket.read_nonblock(MSG_BUF_SIZE)
242
+ end
243
+
244
+ # Get one message from the bus and remove it from the buffer.
245
+ # Return the message.
246
+ def pop_message
247
+ ret = nil
248
+ begin
249
+ ret, size = Message.new.unmarshall_buffer(@buffer)
250
+ @buffer.slice!(0, size)
251
+ rescue IncompleteBufferException => e
252
+ # fall through, let ret be null
253
+ end
254
+ ret
255
+ end
256
+
257
+ # Retrieve all the messages that are currently in the buffer.
258
+ def messages
259
+ ret = Array.new
260
+ while msg = pop_message
261
+ ret << msg
262
+ end
263
+ ret
264
+ end
265
+
266
+ # The buffer size for messages.
267
+ MSG_BUF_SIZE = 4096
268
+
269
+ # Update the buffer and retrieve all messages using Connection#messages.
270
+ # Return the messages.
271
+ def poll_messages
272
+ ret = nil
273
+ r, d, d = IO.select([@socket], nil, nil, 0)
274
+ if r and r.size > 0
275
+ update_buffer
276
+ end
277
+ messages
278
+ end
279
+
280
+ # Wait for a message to arrive. Return it once it is available.
281
+ def wait_for_message
282
+ ret = pop_message
283
+ while ret == nil
284
+ r, d, d = IO.select([@socket])
285
+ if r and r[0] == @socket
286
+ update_buffer
287
+ ret = pop_message
288
+ end
289
+ end
290
+ ret
291
+ end
292
+
293
+ # Send a message _m_ on to the bus. This is done synchronously, thus
294
+ # the call will block until a reply message arrives.
295
+ def send_sync(m, &retc) # :yields: reply/return message
296
+ send(m.marshall)
297
+ @method_call_msgs[m.serial] = m
298
+ @method_call_replies[m.serial] = retc
299
+
300
+ retm = wait_for_message
301
+ process(retm)
302
+ until [DBus::Message::ERROR,
303
+ DBus::Message::METHOD_RETURN].include?(retm.message_type) and
304
+ retm.reply_serial == m.serial
305
+ retm = wait_for_message
306
+ process(retm)
307
+ end
308
+ end
309
+
310
+ # Specify a code block that has to be executed when a reply for
311
+ # message _m_ is received.
312
+ def on_return(m, &retc)
313
+ # Have a better exception here
314
+ if m.message_type != Message::METHOD_CALL
315
+ raise "on_return should only get method_calls"
316
+ end
317
+ @method_call_msgs[m.serial] = m
318
+ @method_call_replies[m.serial] = retc
319
+ end
320
+
321
+ # Asks bus to send us messages matching mr, and execute slot when
322
+ # received
323
+ def add_match(mr, &slot)
324
+ # check this is a signal.
325
+ @signal_matchrules << [mr, slot]
326
+ self.proxy.AddMatch(mr.to_s)
327
+ end
328
+
329
+ # Process a message _m_ based on its type.
330
+ # method call:: FIXME...
331
+ # method call return value:: FIXME...
332
+ # signal:: FIXME...
333
+ # error:: FIXME...
334
+ def process(m)
335
+ case m.message_type
336
+ when Message::ERROR, Message::METHOD_RETURN
337
+ raise InvalidPacketException if m.reply_serial == nil
338
+ mcs = @method_call_replies[m.reply_serial]
339
+ if not mcs
340
+ puts "no return code for #{mcs.inspect} (#{m.inspect})" if $DEBUG
341
+ else
342
+ if m.message_type == Message::ERROR
343
+ mcs.call(Error.new(m))
344
+ else
345
+ mcs.call(m)
346
+ end
347
+ @method_call_replies.delete(m.reply_serial)
348
+ @method_call_msgs.delete(m.reply_serial)
349
+ end
350
+ when DBus::Message::METHOD_CALL
351
+ if m.path == "/org/freedesktop/DBus"
352
+ puts "Got method call on /org/freedesktop/DBus" if $DEBUG
353
+ end
354
+ # handle introspectable as an exception:
355
+ if m.interface == "org.freedesktop.DBus.Introspectable" and
356
+ m.member == "Introspect"
357
+ reply = Message.new(Message::METHOD_RETURN).reply_to(m)
358
+ reply.sender = @unique_name
359
+ node = @service.get_node(m.path)
360
+ raise NotImplementedError if not node
361
+ reply.sender = @unique_name
362
+ reply.add_param(Type::STRING, @service.get_node(m.path).to_xml)
363
+ send(reply.marshall)
364
+ else
365
+ node = @service.get_node(m.path)
366
+ return if node.nil?
367
+ obj = node.object
368
+ return if obj.nil?
369
+ obj.dispatch(m) if obj
370
+ end
371
+ when DBus::Message::SIGNAL
372
+ @signal_matchrules.each do |elem|
373
+ mr, slot = elem
374
+ if mr.match(m)
375
+ slot.call(m)
376
+ return
377
+ end
378
+ end
379
+ else
380
+ puts "Unknown message type: #{m.message_type}" if $DEBUG
381
+ end
382
+ end
383
+
384
+ # Retrieves the service with the given _name_.
385
+ def service(name)
386
+ # The service might not exist at this time so we cannot really check
387
+ # anything
388
+ Service.new(name, self)
389
+ end
390
+ alias :[] :service
391
+
392
+ # Emit a signal event for the given _service_, object _obj_, interface
393
+ # _intf_ and signal _sig_ with arguments _args_.
394
+ def emit(service, obj, intf, sig, *args)
395
+ m = Message.new(DBus::Message::SIGNAL)
396
+ m.path = obj.path
397
+ m.interface = intf.name
398
+ m.member = sig.name
399
+ m.sender = service.name
400
+ i = 0
401
+ sig.params.each do |par|
402
+ m.add_param(par[1], args[i])
403
+ i += 1
404
+ end
405
+ send(m.marshall)
406
+ end
407
+
408
+ ###########################################################################
409
+ private
410
+
411
+ # Send a hello messages to the bus to let it know we are here.
412
+ def send_hello
413
+ m = Message.new(DBus::Message::METHOD_CALL)
414
+ m.path = "/org/freedesktop/DBus"
415
+ m.destination = "org.freedesktop.DBus"
416
+ m.interface = "org.freedesktop.DBus"
417
+ m.member = "Hello"
418
+ send_sync(m) do |rmsg|
419
+ @unique_name = rmsg.destination
420
+ puts "Got hello reply. Our unique_name is #{@unique_name}" if $DEBUG
421
+ end
422
+ end
423
+
424
+ # Parse the session string (socket address).
425
+ def parse_session_string
426
+ path_parsed = /^([^:]*):([^;]*)$/.match(@path)
427
+ @transport = path_parsed[1]
428
+ adr = path_parsed[2]
429
+ if @transport == "unix"
430
+ adr.split(",").each do |eqstr|
431
+ idx, val = eqstr.split("=")
432
+ case idx
433
+ when "path"
434
+ @type = idx
435
+ @unix = val
436
+ when "abstract"
437
+ @type = idx
438
+ @unix_abstract = val
439
+ when "guid"
440
+ @guid = val
441
+ end
442
+ end
443
+ end
444
+ end
445
+
446
+ # Initialize the connection to the bus.
447
+ def init_connection
448
+ @client = Client.new(@socket)
449
+ @client.authenticate
450
+ # TODO: code some real stuff here
451
+ #writel("AUTH EXTERNAL 31303030")
452
+ #s = readl
453
+ # parse OK ?
454
+ #writel("BEGIN")
455
+ end
456
+ end # class Connection
457
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pangdudu-ruby-dbus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruby DBUS Team
@@ -51,6 +51,7 @@ files:
51
51
  - lib/dbus/introspect.rb
52
52
  - lib/dbus/matchrule.rb
53
53
  - lib/dbus/bus.rb
54
+ - lib/dbus/connection.rb
54
55
  - lib/dbus.rb
55
56
  - ChangeLog
56
57
  - COPYING