pangdudu-ruby-dbus 0.2.1 → 0.2.2

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 (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