daemons 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -1
- data/Releases +7 -1
- data/TODO +3 -0
- data/examples/ctrl_crash.rb +1 -0
- data/examples/ctrl_exit.rb +15 -0
- data/examples/ctrl_monitor.rb +16 -0
- data/examples/ctrl_multiple.rb +16 -0
- data/examples/myserver_crashing.rb.output +15 -0
- data/examples/myserver_exiting.rb +8 -0
- data/lib/daemons.rb +102 -29
- data/lib/daemons/cmdline.rb +4 -0
- data/lib/daemons/daemonize.rb +25 -6
- data/lib/daemons/monitor.rb +101 -0
- data/lib/daemons/pidfile.rb +6 -0
- metadata +9 -3
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
= Daemons Version 0.
|
1
|
+
= Daemons Version 0.3.0
|
2
2
|
|
3
3
|
(See Releases for release-specific information)
|
4
4
|
|
@@ -7,6 +7,10 @@
|
|
7
7
|
Daemons provides an easy way to wrap existing ruby scripts (for example a self-written server)
|
8
8
|
to be <i>run as a daemon</i> and to be <i>controlled by simple start/stop/restart commands</i>.
|
9
9
|
|
10
|
+
Besides this basic functionality, daemons offers many advanced features like <i>exception backtracing</i>
|
11
|
+
and logging (in case your ruby script crashes) and <i>monitoring</i> and automatic restarting of your processes
|
12
|
+
if they crash.
|
13
|
+
|
10
14
|
Daemons includes the <tt>daemonize.rb</tt> script written by <i>Travis Whitton</i> to do the daemonization
|
11
15
|
process.
|
12
16
|
|
data/Releases
CHANGED
@@ -4,7 +4,6 @@
|
|
4
4
|
|
5
5
|
* Initial release
|
6
6
|
|
7
|
-
|
8
7
|
== Release 0.2.0: Mar 21, 2005
|
9
8
|
|
10
9
|
* Exception backtrace functionality added
|
@@ -15,3 +14,10 @@
|
|
15
14
|
== Release 0.2.1: Mar 21, 2005
|
16
15
|
|
17
16
|
* Bugfix for a problem with the 'status' command
|
17
|
+
|
18
|
+
== Release 0.3.0: April 21, 2005
|
19
|
+
|
20
|
+
* New monitor functionality: automatic restarting of your applications if they crash
|
21
|
+
* 'restart' command fixed
|
22
|
+
* '--force' command modifier (please refer to the documentation)
|
23
|
+
* Some more bugfixes and improvements
|
data/TODO
CHANGED
data/examples/ctrl_crash.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
|
2
|
+
|
3
|
+
if File.exists?(File.join(lib_dir, 'daemons.rb'))
|
4
|
+
$LOAD_PATH.unshift lib_dir
|
5
|
+
else
|
6
|
+
require 'rubygems' rescue nil
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'daemons'
|
10
|
+
|
11
|
+
|
12
|
+
options = {
|
13
|
+
}
|
14
|
+
|
15
|
+
Daemons.run(File.join(File.split(__FILE__)[0], 'myserver_exiting.rb'), options)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
|
2
|
+
|
3
|
+
if File.exists?(File.join(lib_dir, 'daemons.rb'))
|
4
|
+
$LOAD_PATH.unshift lib_dir
|
5
|
+
else
|
6
|
+
require 'rubygems' rescue nil
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'daemons'
|
10
|
+
|
11
|
+
|
12
|
+
options = {
|
13
|
+
:monitor => true
|
14
|
+
}
|
15
|
+
|
16
|
+
Daemons.run(File.join(File.split(__FILE__)[0], 'myserver_crashing.rb'), options)
|
@@ -0,0 +1,16 @@
|
|
1
|
+
lib_dir = File.expand_path(File.join(File.split(__FILE__)[0], '../lib'))
|
2
|
+
|
3
|
+
if File.exists?(File.join(lib_dir, 'daemons.rb'))
|
4
|
+
$LOAD_PATH.unshift lib_dir
|
5
|
+
else
|
6
|
+
require 'rubygems' rescue nil
|
7
|
+
end
|
8
|
+
|
9
|
+
require 'daemons'
|
10
|
+
|
11
|
+
|
12
|
+
options = {
|
13
|
+
:multiple => true
|
14
|
+
}
|
15
|
+
|
16
|
+
Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'), options)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
/home/uehli/Desktop/daemons-current/examples/myserver_crashing.rb:13: CRASH! (RuntimeError)
|
2
|
+
from /home/uehli/Desktop/daemons-current/examples/myserver_crashing.rb:6:in `loop'
|
3
|
+
from /home/uehli/Desktop/daemons-current/examples/myserver_crashing.rb:6
|
4
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:116:in `load'
|
5
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:116:in `run_via_load'
|
6
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:90:in `start'
|
7
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:359:in `run'
|
8
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:469:in `run'
|
9
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:468:in `call'
|
10
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons/cmdline.rb:94:in `catch_exceptions'
|
11
|
+
from /home/uehli/Desktop/daemons-current/lib/daemons.rb:468:in `run'
|
12
|
+
from ctrl_crash.rb:17
|
13
|
+
ping from myserver.rb!
|
14
|
+
this example server will crash in 3 seconds...
|
15
|
+
CRASH!
|
data/lib/daemons.rb
CHANGED
@@ -4,6 +4,7 @@ require 'optparse/time'
|
|
4
4
|
require 'daemons/pidfile'
|
5
5
|
require 'daemons/cmdline'
|
6
6
|
require 'daemons/exceptions'
|
7
|
+
require 'daemons/monitor'
|
7
8
|
|
8
9
|
|
9
10
|
# All functions and classes that Daemons provides reside in this module.
|
@@ -17,7 +18,7 @@ require 'daemons/exceptions'
|
|
17
18
|
#
|
18
19
|
module Daemons
|
19
20
|
|
20
|
-
VERSION = "0.
|
21
|
+
VERSION = "0.3.0"
|
21
22
|
|
22
23
|
require 'daemons/daemonize'
|
23
24
|
|
@@ -48,11 +49,11 @@ module Daemons
|
|
48
49
|
PidFile.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
|
49
50
|
end
|
50
51
|
|
51
|
-
def
|
52
|
+
def real_start
|
52
53
|
opts = @group.controller.options
|
53
54
|
|
54
55
|
unless opts[:ontop]
|
55
|
-
Daemonize.daemonize()
|
56
|
+
Daemonize.daemonize(opts[:log_output] ? File.join(pidfile_dir(), @group.app_name + '.output') : nil)
|
56
57
|
end
|
57
58
|
|
58
59
|
@pid_file.write
|
@@ -90,6 +91,13 @@ module Daemons
|
|
90
91
|
run_via_load()
|
91
92
|
end
|
92
93
|
end
|
94
|
+
private :real_start
|
95
|
+
|
96
|
+
def start
|
97
|
+
@group.create_monitor(@group.applications[0] || self)
|
98
|
+
|
99
|
+
real_start
|
100
|
+
end
|
93
101
|
|
94
102
|
def run
|
95
103
|
if @group.controller.options[:exec]
|
@@ -132,31 +140,34 @@ module Daemons
|
|
132
140
|
|
133
141
|
|
134
142
|
# the code below only logs the last exception
|
135
|
-
|
143
|
+
# e = nil
|
144
|
+
#
|
145
|
+
# ObjectSpace.each_object {|o|
|
146
|
+
# if ::Exception === o
|
147
|
+
# e = o
|
148
|
+
# end
|
149
|
+
# }
|
150
|
+
#
|
151
|
+
# l_file.error e
|
152
|
+
# l_file.close
|
136
153
|
|
154
|
+
# this code logs every exception found in memory
|
137
155
|
ObjectSpace.each_object {|o|
|
138
156
|
if ::Exception === o
|
139
|
-
|
157
|
+
l_file.error o
|
140
158
|
end
|
141
159
|
}
|
142
160
|
|
143
|
-
l_file.error e
|
144
161
|
l_file.close
|
145
|
-
|
146
|
-
e = nil
|
147
|
-
|
148
|
-
# this code logs every exception found in memory
|
149
|
-
# ObjectSpace.each_object {|o|
|
150
|
-
# if ::Exception === o
|
151
|
-
# l_file.error o
|
152
|
-
# end
|
153
|
-
# }
|
154
|
-
#
|
155
|
-
# l_file.close
|
156
162
|
end
|
157
163
|
|
158
164
|
|
159
165
|
def stop
|
166
|
+
if @group.controller.options[:force] and not running?
|
167
|
+
self.zap
|
168
|
+
return
|
169
|
+
end
|
170
|
+
|
160
171
|
Process.kill('TERM', @pid_file.read)
|
161
172
|
|
162
173
|
# We try to remove the pid-files by ourselves, in case the application
|
@@ -169,10 +180,14 @@ module Daemons
|
|
169
180
|
@pid_file.remove
|
170
181
|
end
|
171
182
|
|
183
|
+
def zap!
|
184
|
+
@pid_file.remove rescue nil
|
185
|
+
end
|
186
|
+
|
172
187
|
def show_status
|
173
188
|
running = self.running?
|
174
189
|
|
175
|
-
puts "#{self.group.app_name}: #{running ? '' : 'not'}
|
190
|
+
puts "#{self.group.app_name}: #{running ? '' : 'not '}running#{(running and @pid_file.exists?) ? ' [pid ' + @pid_file.read.to_s + ']' : ''}#{(@pid_file.exists? and not running) ? ' (but pid-file exists: ' + @pid_file.read.to_s + ')' : ''}"
|
176
191
|
end
|
177
192
|
|
178
193
|
# This function implements a (probably too simle) method to detect
|
@@ -184,7 +199,7 @@ module Daemons
|
|
184
199
|
#
|
185
200
|
def running?
|
186
201
|
if @pid_file.exists?
|
187
|
-
return
|
202
|
+
return PidFile.running?(@pid_file.read)
|
188
203
|
end
|
189
204
|
|
190
205
|
return false
|
@@ -197,6 +212,7 @@ module Daemons
|
|
197
212
|
attr_reader :app_name
|
198
213
|
attr_reader :script
|
199
214
|
|
215
|
+
attr_reader :monitor
|
200
216
|
attr_reader :controller
|
201
217
|
|
202
218
|
attr_reader :applications
|
@@ -215,6 +231,7 @@ module Daemons
|
|
215
231
|
@app_name = app_name
|
216
232
|
@script = script
|
217
233
|
@controller = controller
|
234
|
+
@monitor = nil
|
218
235
|
|
219
236
|
options = controller.options
|
220
237
|
|
@@ -223,6 +240,14 @@ module Daemons
|
|
223
240
|
@dir_mode = options[:dir_mode] || :script
|
224
241
|
@dir = options[:dir] || ''
|
225
242
|
|
243
|
+
#@applications = find_applications(pidfile_dir())
|
244
|
+
end
|
245
|
+
|
246
|
+
# Setup the application group.
|
247
|
+
# Currently this functions calls <tt>find_applications</tt> which finds
|
248
|
+
# all running instances of the application and populates the application array.
|
249
|
+
#
|
250
|
+
def setup
|
226
251
|
@applications = find_applications(pidfile_dir())
|
227
252
|
end
|
228
253
|
|
@@ -235,33 +260,76 @@ module Daemons
|
|
235
260
|
|
236
261
|
#pp pid_files
|
237
262
|
|
238
|
-
|
263
|
+
@monitor = Monitor.find(dir, app_name + '_monitor')
|
264
|
+
|
265
|
+
pid_files.reject! {|f| f =~ /_monitor.pid$/}
|
266
|
+
|
267
|
+
return pid_files.map {|f|
|
268
|
+
app = Application.new(self, PidFile.existing(f))
|
269
|
+
setup_app(app)
|
270
|
+
app
|
271
|
+
}
|
239
272
|
end
|
240
273
|
|
241
274
|
def new_application(script = nil)
|
242
275
|
if @applications.size > 0 and not @multiple
|
243
|
-
|
276
|
+
if @controller.options[:force]
|
277
|
+
@applications.delete_if {|a|
|
278
|
+
unless a.running?
|
279
|
+
a.zap
|
280
|
+
true
|
281
|
+
end
|
282
|
+
}
|
283
|
+
end
|
284
|
+
|
285
|
+
raise RuntimeException.new('there is already one or more instance(s) of the program running') unless @applications.empty?
|
244
286
|
end
|
245
287
|
|
246
288
|
app = Application.new(self)
|
247
289
|
|
248
|
-
app
|
249
|
-
app.app_argv = @app_argv
|
290
|
+
setup_app(app)
|
250
291
|
|
251
292
|
@applications << app
|
252
293
|
|
253
294
|
return app
|
254
295
|
end
|
255
296
|
|
297
|
+
def setup_app(app)
|
298
|
+
app.controller_argv = @controller_argv
|
299
|
+
app.app_argv = @app_argv
|
300
|
+
end
|
301
|
+
private :setup_app
|
302
|
+
|
303
|
+
def create_monitor(an_app)
|
304
|
+
return if @monitor
|
305
|
+
|
306
|
+
if @controller.options[:monitor]
|
307
|
+
@monitor = Monitor.new(an_app)
|
308
|
+
|
309
|
+
@monitor.start(@applications)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
256
313
|
def start_all
|
257
|
-
@
|
314
|
+
@monitor.stop if @monitor
|
315
|
+
@monitor = nil
|
316
|
+
|
317
|
+
@applications.each {|a|
|
318
|
+
fork {
|
319
|
+
a.start
|
320
|
+
}
|
321
|
+
}
|
258
322
|
end
|
259
323
|
|
260
324
|
def stop_all
|
325
|
+
@monitor.stop if @monitor
|
326
|
+
|
261
327
|
@applications.each {|a| a.stop}
|
262
328
|
end
|
263
329
|
|
264
330
|
def zap_all
|
331
|
+
@monitor.stop if @monitor
|
332
|
+
|
265
333
|
@applications.each {|a| a.zap}
|
266
334
|
end
|
267
335
|
|
@@ -323,6 +391,8 @@ module Daemons
|
|
323
391
|
@group.controller_argv = @controller_part
|
324
392
|
@group.app_argv = @app_part
|
325
393
|
|
394
|
+
@group.setup
|
395
|
+
|
326
396
|
case @command
|
327
397
|
when 'start'
|
328
398
|
@group.new_application.start
|
@@ -331,9 +401,11 @@ module Daemons
|
|
331
401
|
when 'stop'
|
332
402
|
@group.stop_all
|
333
403
|
when 'restart'
|
334
|
-
@group.
|
335
|
-
|
336
|
-
|
404
|
+
unless @group.applications.empty?
|
405
|
+
@group.stop_all
|
406
|
+
sleep 1
|
407
|
+
@group.start_all
|
408
|
+
end
|
337
409
|
when 'zap'
|
338
410
|
@group.zap_all
|
339
411
|
when 'status'
|
@@ -412,7 +484,7 @@ module Daemons
|
|
412
484
|
# but by exec'ing the script file
|
413
485
|
# <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
|
414
486
|
# pid-file directory if the application exits due to an uncaught exception
|
415
|
-
#
|
487
|
+
# <tt>:monitor</tt>:: Monitor the programs and restart crashed instances
|
416
488
|
# -----
|
417
489
|
#
|
418
490
|
# === Example:
|
@@ -422,7 +494,8 @@ module Daemons
|
|
422
494
|
# :multiple => true,
|
423
495
|
# :ontop => true,
|
424
496
|
# :exec => true,
|
425
|
-
# :backtrace => true
|
497
|
+
# :backtrace => true,
|
498
|
+
# :monitor => true
|
426
499
|
# }
|
427
500
|
#
|
428
501
|
# Daemons.run(File.join(File.split(__FILE__)[0], 'myscript.rb'), options)
|
data/lib/daemons/cmdline.rb
CHANGED
data/lib/daemons/daemonize.rb
CHANGED
@@ -113,7 +113,7 @@ module Daemonize
|
|
113
113
|
|
114
114
|
|
115
115
|
# This method causes the current running process to become a daemon
|
116
|
-
def daemonize(oldmode=0)
|
116
|
+
def daemonize(logfile_name = nil, oldmode=0)
|
117
117
|
srand # Split rand streams between spawning and daemonized process
|
118
118
|
safefork and exit # Fork and exit from the parent
|
119
119
|
|
@@ -134,15 +134,34 @@ module Daemonize
|
|
134
134
|
# Make sure all file descriptors are closed
|
135
135
|
ObjectSpace.each_object(IO) do |io|
|
136
136
|
unless [STDIN, STDOUT, STDERR].include?(io)
|
137
|
-
|
138
|
-
io.
|
137
|
+
begin
|
138
|
+
unless io.closed?
|
139
|
+
io.close
|
140
|
+
end
|
141
|
+
rescue ::Exception
|
139
142
|
end
|
140
143
|
end
|
141
144
|
end
|
142
145
|
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
+
# Free file descriptors and
|
147
|
+
# point them somewhere sensible
|
148
|
+
# STDOUT/STDERR should go to a logfile
|
149
|
+
|
150
|
+
STDIN.reopen "/dev/null" rescue nil
|
151
|
+
|
152
|
+
if logfile_name
|
153
|
+
begin
|
154
|
+
STDOUT.reopen logfile_name, "a"
|
155
|
+
rescue ::Exception
|
156
|
+
STDOUT.reopen "/dev/null" rescue nil
|
157
|
+
end
|
158
|
+
else
|
159
|
+
STDOUT.reopen "/dev/null" rescue nil
|
160
|
+
end
|
161
|
+
|
162
|
+
STDERR.reopen STDOUT rescue nil
|
163
|
+
|
164
|
+
|
146
165
|
return oldmode ? sess_id : 0 # Return value is mostly irrelevant
|
147
166
|
end
|
148
167
|
module_function :daemonize
|
@@ -0,0 +1,101 @@
|
|
1
|
+
|
2
|
+
module Daemons
|
3
|
+
|
4
|
+
require 'daemons/daemonize'
|
5
|
+
|
6
|
+
class Monitor
|
7
|
+
|
8
|
+
def self.find(dir, app_name)
|
9
|
+
pid_file = PidFile.find_files(dir, app_name)[0]
|
10
|
+
|
11
|
+
if pid_file
|
12
|
+
pid_file = PidFile.existing(pid_file)
|
13
|
+
|
14
|
+
unless PidFile.running?(pid_file.read)
|
15
|
+
pid_file.remove rescue nil
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
monitor = self.allocate
|
20
|
+
|
21
|
+
monitor.instance_variable_set(:@pid_file, pid_file)
|
22
|
+
|
23
|
+
return monitor
|
24
|
+
end
|
25
|
+
|
26
|
+
return nil
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def initialize(an_app)
|
31
|
+
@pid_file = PidFile.new(an_app.pidfile_dir, an_app.group.app_name + '_monitor', false)
|
32
|
+
end
|
33
|
+
|
34
|
+
def start(applications)
|
35
|
+
return if applications.empty?
|
36
|
+
|
37
|
+
fork do
|
38
|
+
Daemonize.daemonize
|
39
|
+
|
40
|
+
begin
|
41
|
+
@pid_file.write
|
42
|
+
|
43
|
+
# at_exit {
|
44
|
+
# @pid_file.remove rescue nil
|
45
|
+
# }
|
46
|
+
|
47
|
+
# This part is needed to remove the pid-file if the application is killed by
|
48
|
+
# daemons or manually by the user.
|
49
|
+
# Note that the applications is not supposed to overwrite the signal handler for
|
50
|
+
# 'TERM'.
|
51
|
+
#
|
52
|
+
# trap('TERM') {
|
53
|
+
# @pid_file.remove rescue nil
|
54
|
+
# exit
|
55
|
+
# }
|
56
|
+
|
57
|
+
sleep(60)
|
58
|
+
|
59
|
+
loop do
|
60
|
+
applications.each {|a|
|
61
|
+
sleep(10)
|
62
|
+
|
63
|
+
unless a.running?
|
64
|
+
a.zap!
|
65
|
+
|
66
|
+
Process.detach(fork { a.start })
|
67
|
+
|
68
|
+
sleep(10)
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
72
|
+
sleep(30)
|
73
|
+
end
|
74
|
+
rescue ::Exception => e
|
75
|
+
begin
|
76
|
+
File.open(File.join(@pid_file.dir, @pid_file.progname + '.log'), 'a') {|f|
|
77
|
+
f.puts Time.now
|
78
|
+
f.puts e
|
79
|
+
f.puts e.backtrace.inspect
|
80
|
+
}
|
81
|
+
ensure
|
82
|
+
@pid_file.remove rescue nil
|
83
|
+
exit!
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def stop
|
92
|
+
Process.kill('TERM', @pid_file.read) rescue nil
|
93
|
+
|
94
|
+
# We try to remove the pid-files by ourselves, in case the application
|
95
|
+
# didn't clean it up.
|
96
|
+
@pid_file.remove rescue nil
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/lib/daemons/pidfile.rb
CHANGED
@@ -40,6 +40,12 @@ module Daemons
|
|
40
40
|
end
|
41
41
|
|
42
42
|
|
43
|
+
def PidFile.running?(pid, additional = nil)
|
44
|
+
output = `ps ax`
|
45
|
+
return (/#{pid} / =~ output and (additional ? /#{additional}/ =~ output : true))
|
46
|
+
end
|
47
|
+
|
48
|
+
|
43
49
|
# Returns the directory that should be used to write the pid file to
|
44
50
|
# depending on the given mode.
|
45
51
|
#
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.8
|
|
3
3
|
specification_version: 1
|
4
4
|
name: daemons
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2005-
|
6
|
+
version: 0.3.0
|
7
|
+
date: 2005-04-21
|
8
8
|
summary: A toolkit to convert your script to a controllable daemon
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -37,16 +37,22 @@ files:
|
|
37
37
|
- lib/daemons/exceptions.rb
|
38
38
|
- lib/daemons/cmdline.rb
|
39
39
|
- lib/daemons/pidfile.rb
|
40
|
+
- lib/daemons/monitor.rb
|
41
|
+
- test/tmp
|
40
42
|
- test/testapp.rb
|
41
43
|
- test/tc_main.rb
|
42
44
|
- test/test1.rb
|
43
|
-
- test/tmp
|
44
45
|
- examples/myserver.rb
|
45
46
|
- examples/myserver_crashing.rb
|
46
47
|
- examples/ctrl_crash.rb
|
47
48
|
- examples/ctrl_ontop.rb
|
48
49
|
- examples/ctrl_exec.rb
|
49
50
|
- examples/ctrl_normal.rb
|
51
|
+
- examples/ctrl_multiple.rb
|
52
|
+
- examples/myserver_exiting.rb
|
53
|
+
- examples/ctrl_exit.rb
|
54
|
+
- examples/myserver_crashing.rb.output
|
55
|
+
- examples/ctrl_monitor.rb
|
50
56
|
test_files:
|
51
57
|
- test/tc_main.rb
|
52
58
|
rdoc_options: []
|