circus-deployment 0.0.1

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