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/lib/dbus/bus.rb ADDED
@@ -0,0 +1,752 @@
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
+ require 'socket'
11
+ require 'thread'
12
+ require 'singleton'
13
+
14
+ # = D-Bus main module
15
+ #
16
+ # Module containing all the D-Bus modules and classes.
17
+ module DBus
18
+ # This represents a remote service. It should not be instancied directly
19
+ # Use Bus::service()
20
+ class Service
21
+ # The service name.
22
+ attr_reader :name
23
+ # The bus the service is running on.
24
+ attr_reader :bus
25
+ # The service root (FIXME).
26
+ attr_reader :root
27
+
28
+ # Create a new service with a given _name_ on a given _bus_.
29
+ def initialize(name, bus)
30
+ @name, @bus = name, bus
31
+ @root = Node.new("/")
32
+ end
33
+
34
+ # Determine whether the service name already exists.
35
+ def exists?
36
+ bus.proxy.ListNames[0].member?(@name)
37
+ end
38
+
39
+ # Perform an introspection on all the objects on the service
40
+ # (starting recursively from the root).
41
+ def introspect
42
+ if block_given?
43
+ raise NotImplementedError
44
+ else
45
+ rec_introspect(@root, "/")
46
+ end
47
+ self
48
+ end
49
+
50
+ # Retrieves an object at the given _path_.
51
+ def object(path)
52
+ node = get_node(path, true)
53
+ if node.object.nil?
54
+ node.object = ProxyObject.new(@bus, @name, path)
55
+ end
56
+ node.object
57
+ end
58
+
59
+ # Export an object _obj_ (an DBus::Object subclass instance).
60
+ def export(obj)
61
+ obj.service = self
62
+ get_node(obj.path, true).object = obj
63
+ end
64
+
65
+ # Get the object node corresponding to the given _path_. if _create_ is
66
+ # true, the the nodes in the path are created if they do not already exist.
67
+ def get_node(path, create = false)
68
+ n = @root
69
+ path.sub(/^\//, "").split("/").each do |elem|
70
+ if not n[elem]
71
+ if not create
72
+ return nil
73
+ else
74
+ n[elem] = Node.new(elem)
75
+ end
76
+ end
77
+ n = n[elem]
78
+ end
79
+ if n.nil?
80
+ wlog "Unknown object #{path.inspect}"
81
+ end
82
+ n
83
+ end
84
+
85
+ #########
86
+ private
87
+ #########
88
+
89
+ # Perform a recursive retrospection on the given current _node_
90
+ # on the given _path_.
91
+ def rec_introspect(node, path)
92
+ xml = bus.introspect_data(@name, path)
93
+ intfs, subnodes = IntrospectXMLParser.new(xml).parse
94
+ subnodes.each do |nodename|
95
+ subnode = node[nodename] = Node.new(nodename)
96
+ if path == "/"
97
+ subpath = "/" + nodename
98
+ else
99
+ subpath = path + "/" + nodename
100
+ end
101
+ rec_introspect(subnode, subpath)
102
+ end
103
+ if intfs.size > 0
104
+ node.object = ProxyObjectFactory.new(xml, @bus, @name, path).build
105
+ end
106
+ end
107
+ end
108
+
109
+ # = Object path node class
110
+ #
111
+ # Class representing a node on an object path.
112
+ class Node < Hash
113
+ # The D-Bus object contained by the node.
114
+ attr_accessor :object
115
+ # The name of the node.
116
+ attr_reader :name
117
+
118
+ # Create a new node with a given _name_.
119
+ def initialize(name)
120
+ @name = name
121
+ @object = nil
122
+ end
123
+
124
+ # Return an XML string representation of the node.
125
+ def to_xml
126
+ xml = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
127
+ <node>'
128
+
129
+ self.each_pair do |k, v|
130
+ xml += "<node name=\"#{k}\" />"
131
+ end
132
+ if @object
133
+ @object.intfs.each_pair do |k, v|
134
+ xml += %{<interface name="#{v.name}">\n}
135
+ v.methods.each_value { |m| xml += m.to_xml }
136
+ v.signals.each_value { |m| xml += m.to_xml }
137
+ xml +="</interface>\n"
138
+ end
139
+ end
140
+ xml += '</node>'
141
+ return xml
142
+ end
143
+
144
+ # Return inspect information of the node.
145
+ def inspect
146
+ # Need something here
147
+ "<DBus::Node #{sub_inspect}>"
148
+ end
149
+
150
+ # Return instance inspect information, used by Node#inspect.
151
+ def sub_inspect
152
+ s = ""
153
+ if not @object.nil?
154
+ s += "%x " % @object.object_id
155
+ end
156
+ s + "{" + keys.collect { |k| "#{k} => #{self[k].sub_inspect}" }.join(",") + "}"
157
+ end
158
+ end # class Inspect
159
+
160
+ # FIXME: rename Connection to Bus?
161
+
162
+ # D-Bus main connection class
163
+ #
164
+ # Main class that maintains a connection to a bus and can handle incoming
165
+ # and outgoing messages.
166
+ class Connection
167
+ # The unique name (by specification) of the message.
168
+ attr_reader :unique_name
169
+ # The socket that is used to connect with the bus.
170
+ attr_reader :socket
171
+
172
+ # Create a new connection to the bus for a given connect _path_. _path_
173
+ # format is described in the D-Bus specification:
174
+ # http://dbus.freedesktop.org/doc/dbus-specification.html#addresses
175
+ # and is something like:
176
+ # "transport1:key1=value1,key2=value2;transport2:key1=value1,key2=value2"
177
+ # e.g. "unix:path=/tmp/dbus-test" or "tcp:host=localhost,port=2687"
178
+ def initialize(path)
179
+ dlog "path: #{path}"
180
+ @path = path
181
+ @unique_name = nil
182
+ @buffer = ""
183
+ @method_call_replies = Hash.new
184
+ @method_call_msgs = Hash.new
185
+ @signal_matchrules = Array.new
186
+ @proxy = nil
187
+ @object_root = Node.new("/")
188
+ @is_tcp = false
189
+ end
190
+
191
+ # Connect to the bus and initialize the connection.
192
+ def connect
193
+ connect_to_tcp if @path.include? "tcp:" #connect to tcp socket
194
+ connect_to_unix if @path.include? "unix:" #connect to unix socket
195
+ end
196
+
197
+ # Connect to a bus over tcp and initialize the connection.
198
+ def connect_to_tcp
199
+ #check if the path is sufficient
200
+ if @path.include? "host=" and @path.include? "port="
201
+ host,port,family = "","",""
202
+ #get the parameters
203
+ @path.split(",").each do |para|
204
+ host = para.sub("tcp:","").sub("host=","") if para.include? "host="
205
+ port = para.sub("port=","").to_i if para.include? "port="
206
+ family = para.sub("family=","") if para.include? "family="
207
+ end
208
+ #dlog "host,port,family : #{host},#{port},#{family}"
209
+ begin
210
+ #initialize the tcp socket
211
+ @socket = TCPSocket.new(host,port)
212
+ init_connection
213
+ @is_tcp = true
214
+ rescue
215
+ elog "Could not establish connection to: #{@path}, will now exit."
216
+ exit(0) #a little harsh
217
+ end
218
+ else
219
+ #Danger, Will Robinson: the specified "path" is not usable
220
+ elog "supplied path: #{@path}, unusable! sorry."
221
+ end
222
+ end
223
+
224
+ # Connect to an abstract unix bus and initialize the connection.
225
+ def connect_to_unix
226
+ @socket = Socket.new(Socket::Constants::PF_UNIX,Socket::Constants::SOCK_STREAM, 0)
227
+ parse_session_string
228
+ if @transport == "unix" and @type == "abstract"
229
+ if HOST_END == LIL_END
230
+ sockaddr = "\1\0\0#{@unix_abstract}"
231
+ else
232
+ sockaddr = "\0\1\0#{@unix_abstract}"
233
+ end
234
+ elsif @transport == "unix" and @type == "path"
235
+ sockaddr = Socket.pack_sockaddr_un(@unix)
236
+ end
237
+ @socket.connect(sockaddr)
238
+ init_connection
239
+ end
240
+
241
+ # Parse the session string (socket address).
242
+ def parse_session_string
243
+ path_parsed = /^([^:]*):([^;]*)$/.match(@path)
244
+ @transport = path_parsed[1]
245
+ adr = path_parsed[2]
246
+ if @transport == "unix"
247
+ adr.split(",").each do |eqstr|
248
+ idx, val = eqstr.split("=")
249
+ case idx
250
+ when "path"
251
+ @type = idx
252
+ @unix = val
253
+ when "abstract"
254
+ @type = idx
255
+ @unix_abstract = val
256
+ when "guid"
257
+ @guid = val
258
+ end
259
+ end
260
+ end
261
+ end
262
+
263
+ # Send the buffer _buf_ to the bus using Connection#writel.
264
+ def send(buf)
265
+ @socket.write(buf) unless @socket.nil?
266
+ end
267
+
268
+ # Tell a bus to register itself on the glib main loop
269
+ def glibize
270
+ require 'glib2'
271
+ # Circumvent a ruby-glib bug
272
+ @channels ||= Array.new
273
+
274
+ gio = GLib::IOChannel.new(@socket.fileno)
275
+ @channels << gio
276
+ gio.add_watch(GLib::IOChannel::IN) do |c, ch|
277
+ update_buffer
278
+ messages.each do |msg|
279
+ process(msg)
280
+ end
281
+ true
282
+ end
283
+ end
284
+
285
+ # FIXME: describe the following names, flags and constants.
286
+ # See DBus spec for definition
287
+ NAME_FLAG_ALLOW_REPLACEMENT = 0x1
288
+ NAME_FLAG_REPLACE_EXISTING = 0x2
289
+ NAME_FLAG_DO_NOT_QUEUE = 0x4
290
+
291
+ REQUEST_NAME_REPLY_PRIMARY_OWNER = 0x1
292
+ REQUEST_NAME_REPLY_IN_QUEUE = 0x2
293
+ REQUEST_NAME_REPLY_EXISTS = 0x3
294
+ REQUEST_NAME_REPLY_ALREADY_OWNER = 0x4
295
+
296
+ DBUSXMLINTRO = '<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
297
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
298
+ <node>
299
+ <interface name="org.freedesktop.DBus.Introspectable">
300
+ <method name="Introspect">
301
+ <arg name="data" direction="out" type="s"/>
302
+ </method>
303
+ </interface>
304
+ <interface name="org.freedesktop.DBus">
305
+ <method name="RequestName">
306
+ <arg direction="in" type="s"/>
307
+ <arg direction="in" type="u"/>
308
+ <arg direction="out" type="u"/>
309
+ </method>
310
+ <method name="ReleaseName">
311
+ <arg direction="in" type="s"/>
312
+ <arg direction="out" type="u"/>
313
+ </method>
314
+ <method name="StartServiceByName">
315
+ <arg direction="in" type="s"/>
316
+ <arg direction="in" type="u"/>
317
+ <arg direction="out" type="u"/>
318
+ </method>
319
+ <method name="Hello">
320
+ <arg direction="out" type="s"/>
321
+ </method>
322
+ <method name="NameHasOwner">
323
+ <arg direction="in" type="s"/>
324
+ <arg direction="out" type="b"/>
325
+ </method>
326
+ <method name="ListNames">
327
+ <arg direction="out" type="as"/>
328
+ </method>
329
+ <method name="ListActivatableNames">
330
+ <arg direction="out" type="as"/>
331
+ </method>
332
+ <method name="AddMatch">
333
+ <arg direction="in" type="s"/>
334
+ </method>
335
+ <method name="RemoveMatch">
336
+ <arg direction="in" type="s"/>
337
+ </method>
338
+ <method name="GetNameOwner">
339
+ <arg direction="in" type="s"/>
340
+ <arg direction="out" type="s"/>
341
+ </method>
342
+ <method name="ListQueuedOwners">
343
+ <arg direction="in" type="s"/>
344
+ <arg direction="out" type="as"/>
345
+ </method>
346
+ <method name="GetConnectionUnixUser">
347
+ <arg direction="in" type="s"/>
348
+ <arg direction="out" type="u"/>
349
+ </method>
350
+ <method name="GetConnectionUnixProcessID">
351
+ <arg direction="in" type="s"/>
352
+ <arg direction="out" type="u"/>
353
+ </method>
354
+ <method name="GetConnectionSELinuxSecurityContext">
355
+ <arg direction="in" type="s"/>
356
+ <arg direction="out" type="ay"/>
357
+ </method>
358
+ <method name="ReloadConfig">
359
+ </method>
360
+ <signal name="NameOwnerChanged">
361
+ <arg type="s"/>
362
+ <arg type="s"/>
363
+ <arg type="s"/>
364
+ </signal>
365
+ <signal name="NameLost">
366
+ <arg type="s"/>
367
+ </signal>
368
+ <signal name="NameAcquired">
369
+ <arg type="s"/>
370
+ </signal>
371
+ </interface>
372
+ </node>
373
+ '
374
+
375
+ def introspect_data(dest, path)
376
+ m = DBus::Message.new(DBus::Message::METHOD_CALL)
377
+ m.path = path
378
+ m.interface = "org.freedesktop.DBus.Introspectable"
379
+ m.destination = dest
380
+ m.member = "Introspect"
381
+ m.sender = unique_name
382
+ if not block_given?
383
+ # introspect in synchronous !
384
+ send_sync(m) do |rmsg|
385
+ if rmsg.is_a?(Error)
386
+ raise rmsg
387
+ else
388
+ return rmsg.params[0]
389
+ end
390
+ end
391
+ else
392
+ send(m.marshall)
393
+ on_return(m) do |rmsg|
394
+ if rmsg.is_a?(Error)
395
+ yield rmsg
396
+ else
397
+ yield rmsg.params[0]
398
+ end
399
+ end
400
+ end
401
+ nil
402
+ end
403
+
404
+ # Issues a call to the org.freedesktop.DBus.Introspectable.Introspect method
405
+ # _dest_ is the service and _path_ the object path you want to introspect
406
+ # If a code block is given, the introspect call in asynchronous. If not
407
+ # data is returned
408
+ #
409
+ # FIXME: link to ProxyObject data definition
410
+ # The returned object is a ProxyObject that has methods you can call to
411
+ # issue somme METHOD_CALL messages, and to setup to receive METHOD_RETURN
412
+ def introspect(dest, path)
413
+ if not block_given?
414
+ # introspect in synchronous !
415
+ data = introspect_data(dest, path)
416
+ pof = DBus::ProxyObjectFactory.new(data, self, dest, path)
417
+ return pof.build
418
+ else
419
+ introspect_data(dest, path) do |data|
420
+ yield(DBus::ProxyObjectFactory.new(data, self, dest, path).build)
421
+ end
422
+ end
423
+ end
424
+
425
+ # Exception raised when a service name is requested that is not available.
426
+ class NameRequestError < Exception
427
+ end
428
+
429
+ # Attempt to request a service _name_.
430
+ def request_service(name)
431
+ r = proxy.RequestName(name, NAME_FLAG_REPLACE_EXISTING)
432
+ raise NameRequestError if r[0] != REQUEST_NAME_REPLY_PRIMARY_OWNER
433
+ @service = Service.new(name, self)
434
+ @service
435
+ end
436
+
437
+ # Set up a ProxyObject for the bus itself, since the bus is introspectable.
438
+ # Returns the object.
439
+ def proxy
440
+ if @proxy == nil
441
+ path = "/org/freedesktop/DBus"
442
+ dest = "org.freedesktop.DBus"
443
+ pof = DBus::ProxyObjectFactory.new(DBUSXMLINTRO, self, dest, path)
444
+ @proxy = pof.build["org.freedesktop.DBus"]
445
+ end
446
+ @proxy
447
+ end
448
+
449
+ # Fill (append) the buffer from data that might be available on the
450
+ # socket.
451
+ def update_buffer
452
+ @buffer += @socket.read_nonblock(MSG_BUF_SIZE) if @is_tcp
453
+ unless @is_tcp
454
+ begin
455
+ @buffer += @socket.read_nonblock(MSG_BUF_SIZE)
456
+ rescue
457
+ wlog ".read_nonblock failed, falling back to .recv"
458
+ @buffer += @socket.recv(MSG_BUF_SIZE)
459
+ end
460
+ end
461
+ end
462
+
463
+ # Get one message from the bus and remove it from the buffer.
464
+ # Return the message.
465
+ def pop_message
466
+ ret = nil
467
+ begin
468
+ ret, size = Message.new.unmarshall_buffer(@buffer)
469
+ @buffer.slice!(0, size)
470
+ rescue IncompleteBufferException => e
471
+ # fall through, let ret be null
472
+ end
473
+ ret
474
+ end
475
+
476
+ # Retrieve all the messages that are currently in the buffer.
477
+ def messages
478
+ ret = Array.new
479
+ while msg = pop_message
480
+ ret << msg
481
+ end
482
+ ret
483
+ end
484
+
485
+ # The buffer size for messages.
486
+ MSG_BUF_SIZE = 4096
487
+
488
+ # Update the buffer and retrieve all messages using Connection#messages.
489
+ # Return the messages.
490
+ def poll_messages
491
+ ret = nil
492
+ r, d, d = IO.select([@socket], nil, nil, 0)
493
+ if r and r.size > 0
494
+ update_buffer
495
+ end
496
+ messages
497
+ end
498
+
499
+ # Wait for a message to arrive. Return it once it is available.
500
+ def wait_for_message
501
+ if @socket.nil?
502
+ elog "Can't wait for messages, @socket is nil."
503
+ return
504
+ end
505
+ ret = pop_message
506
+ while ret == nil
507
+ r, d, d = IO.select([@socket])
508
+ if r and r[0] == @socket
509
+ update_buffer
510
+ ret = pop_message
511
+ end
512
+ end
513
+ ret
514
+ end
515
+
516
+ # Send a message _m_ on to the bus. This is done synchronously, thus
517
+ # the call will block until a reply message arrives.
518
+ def send_sync(m, &retc) # :yields: reply/return message
519
+ return if m.nil? #check if somethings wrong
520
+ send(m.marshall)
521
+ @method_call_msgs[m.serial] = m
522
+ @method_call_replies[m.serial] = retc
523
+
524
+ retm = wait_for_message
525
+
526
+ return if retm.nil? #check if somethings wrong
527
+
528
+ process(retm)
529
+ until [DBus::Message::ERROR,
530
+ DBus::Message::METHOD_RETURN].include?(retm.message_type) and
531
+ retm.reply_serial == m.serial
532
+ retm = wait_for_message
533
+ process(retm)
534
+ end
535
+ end
536
+
537
+ # Specify a code block that has to be executed when a reply for
538
+ # message _m_ is received.
539
+ def on_return(m, &retc)
540
+ # Have a better exception here
541
+ if m.message_type != Message::METHOD_CALL
542
+ elog "Funky exception, occured."
543
+ raise "on_return should only get method_calls"
544
+ end
545
+ @method_call_msgs[m.serial] = m
546
+ @method_call_replies[m.serial] = retc
547
+ end
548
+
549
+ # Asks bus to send us messages matching mr, and execute slot when
550
+ # received
551
+ def add_match(mr, &slot)
552
+ # check this is a signal.
553
+ @signal_matchrules << [mr, slot]
554
+ self.proxy.AddMatch(mr.to_s)
555
+ end
556
+
557
+ # Process a message _m_ based on its type.
558
+ def process(m)
559
+ return if m.nil? #check if somethings wrong
560
+ case m.message_type
561
+ when Message::ERROR, Message::METHOD_RETURN
562
+ raise InvalidPacketException if m.reply_serial == nil
563
+ mcs = @method_call_replies[m.reply_serial]
564
+ if not mcs
565
+ dlog "no return code for mcs: #{mcs.inspect} m: #{m.inspect}"
566
+ else
567
+ if m.message_type == Message::ERROR
568
+ mcs.call(Error.new(m))
569
+ else
570
+ mcs.call(m)
571
+ end
572
+ @method_call_replies.delete(m.reply_serial)
573
+ @method_call_msgs.delete(m.reply_serial)
574
+ end
575
+ when DBus::Message::METHOD_CALL
576
+ if m.path == "/org/freedesktop/DBus"
577
+ dlog "Got method call on /org/freedesktop/DBus"
578
+ end
579
+ # handle introspectable as an exception:
580
+ if m.interface == "org.freedesktop.DBus.Introspectable" and
581
+ m.member == "Introspect"
582
+ reply = Message.new(Message::METHOD_RETURN).reply_to(m)
583
+ reply.sender = @unique_name
584
+ node = @service.get_node(m.path)
585
+ raise NotImplementedError if not node
586
+ reply.sender = @unique_name
587
+ reply.add_param(Type::STRING, @service.get_node(m.path).to_xml)
588
+ send(reply.marshall)
589
+ else
590
+ node = @service.get_node(m.path)
591
+ return if node.nil?
592
+ obj = node.object
593
+ return if obj.nil?
594
+ obj.dispatch(m) if obj
595
+ end
596
+ when DBus::Message::SIGNAL
597
+ @signal_matchrules.each do |elem|
598
+ mr, slot = elem
599
+ if mr.match(m)
600
+ slot.call(m)
601
+ return
602
+ end
603
+ end
604
+ else
605
+ dlog "Unknown message type: #{m.message_type}"
606
+ end
607
+ end
608
+
609
+ # Retrieves the service with the given _name_.
610
+ def service(name)
611
+ # The service might not exist at this time so we cannot really check
612
+ # anything
613
+ Service.new(name, self)
614
+ end
615
+ alias :[] :service
616
+
617
+ # Emit a signal event for the given _service_, object _obj_, interface
618
+ # _intf_ and signal _sig_ with arguments _args_.
619
+ def emit(service, obj, intf, sig, *args)
620
+ m = Message.new(DBus::Message::SIGNAL)
621
+ m.path = obj.path
622
+ m.interface = intf.name
623
+ m.member = sig.name
624
+ m.sender = service.name
625
+ i = 0
626
+ sig.params.each do |par|
627
+ m.add_param(par[1], args[i])
628
+ i += 1
629
+ end
630
+ send(m.marshall)
631
+ end
632
+
633
+ ###########################################################################
634
+ private
635
+
636
+ # Send a hello messages to the bus to let it know we are here.
637
+ def send_hello
638
+ m = Message.new(DBus::Message::METHOD_CALL)
639
+ m.path = "/org/freedesktop/DBus"
640
+ m.destination = "org.freedesktop.DBus"
641
+ m.interface = "org.freedesktop.DBus"
642
+ m.member = "Hello"
643
+ send_sync(m) do |rmsg|
644
+ @unique_name = rmsg.destination
645
+ dlog "Got hello reply. Our unique_name is #{@unique_name}, i feel special."
646
+ end
647
+ end
648
+
649
+ # Initialize the connection to the bus.
650
+ def init_connection
651
+ @client = Client.new(@socket)
652
+ @client.authenticate
653
+ end
654
+ end # class Connection
655
+
656
+ # = D-Bus session bus class
657
+ #
658
+ # The session bus is a session specific bus (mostly for desktop use).
659
+ # This is a singleton class.
660
+ class SessionBus < Connection
661
+ include Singleton
662
+
663
+ # Get the the default session bus.
664
+ def initialize socket_name=SessionSocketName
665
+ super(socket_name)
666
+ connect
667
+ send_hello
668
+ end
669
+ end
670
+
671
+ # = D-Bus system bus class
672
+ #
673
+ # The system bus is a system-wide bus mostly used for global or
674
+ # system usages. This is a singleton class.
675
+ class SystemBus < Connection
676
+ include Singleton
677
+
678
+ # Get the default system bus.
679
+ def initialize socket_name=SystemSocketName
680
+ super(socket_name)
681
+ connect
682
+ send_hello
683
+ end
684
+ end
685
+
686
+ # = D-Bus remote (TCP) bus class
687
+ #
688
+ # This class may be used when connecting to remote (listening on a TCP socket)
689
+ # busses. You can also use it to connect to other non-standard path busses.
690
+ #
691
+ # The specified socket_name should look like this:
692
+ # (for TCP) tcp:host=127.0.0.1,port=2687
693
+ # (for Unix-socket) unix:path=/tmp/my_funky_bus_socket
694
+ #
695
+ # you'll need to take care about authentification then, more info here:
696
+ # http://github.com/pangdudu/ruby-dbus/blob/master/README.rdoc
697
+ class RemoteBus < Connection
698
+
699
+ # Get the remote bus.
700
+ def initialize socket_name
701
+ super(socket_name)
702
+ connect
703
+ send_hello
704
+ end
705
+ end
706
+
707
+
708
+ # FIXME: we should get rid of these singeltons
709
+
710
+ def DBus.system_bus
711
+ SystemBus.instance
712
+ end
713
+
714
+ def DBus.session_bus
715
+ SessionBus.instance
716
+ end
717
+
718
+ # = Main event loop class.
719
+ #
720
+ # Class that takes care of handling message and signal events
721
+ # asynchronously. *Note:* This is a native implement and therefore does
722
+ # not integrate with a graphical widget set main loop.
723
+ class Main
724
+ # Create a new main event loop.
725
+ def initialize
726
+ @buses = Hash.new
727
+ end
728
+
729
+ # Add a _bus_ to the list of buses to watch for events.
730
+ def <<(bus)
731
+ @buses[bus.socket] = bus
732
+ end
733
+
734
+ # Run the main loop. This is a blocking call!
735
+ def run
736
+ loop do
737
+ ready, dum, dum = IO.select(@buses.keys)
738
+ ready.each do |socket|
739
+ b = @buses[socket]
740
+ begin
741
+ b.update_buffer
742
+ rescue EOFError
743
+ return # the bus died
744
+ end
745
+ while m = b.pop_message
746
+ b.process(m)
747
+ end
748
+ end
749
+ end
750
+ end
751
+ end # class Main
752
+ end # module DBus