circus-deployment 0.0.1

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