daemons 0.0.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/Rakefile +5 -4
- data/Releases +8 -1
- data/TODO +2 -1
- data/examples/ctrl_crash.rb +16 -0
- data/examples/ctrl_exec.rb +16 -0
- data/examples/ctrl_normal.rb +12 -0
- data/examples/ctrl_ontop.rb +16 -0
- data/examples/myserver.rb +12 -0
- data/examples/myserver_crashing.rb +14 -0
- data/lib/daemons.rb +162 -29
- data/lib/daemons/cmdline.rb +1 -0
- data/lib/daemons/daemonize.rb +7 -3
- metadata +9 -3
data/README
CHANGED
data/Rakefile
CHANGED
@@ -16,14 +16,15 @@ PKG_FILES = FileList[
|
|
16
16
|
"Rakefile", "Releases", "TODO", "README",
|
17
17
|
"setup.rb",
|
18
18
|
"lib/**/*.rb",
|
19
|
-
"test/**/*"
|
19
|
+
"test/**/*",
|
20
|
+
"examples/**/*"
|
20
21
|
]
|
21
|
-
PKG_FILES.exclude(%r(^test/tmp/.+))
|
22
|
-
|
22
|
+
#PKG_FILES.exclude(%r(^test/tmp/.+))
|
23
|
+
PKG_FILES.exclude(%r(\.pid$))
|
24
|
+
PKG_FILES.exclude(%r(\.log$))
|
23
25
|
|
24
26
|
spec = Gem::Specification.new do |s|
|
25
27
|
s.name = PKG_NAME
|
26
|
-
#s.version = "0.0.1"
|
27
28
|
s.version = Daemons::VERSION
|
28
29
|
s.author = "Thomas Uehlinger"
|
29
30
|
s.email = "th.uehlinger@gmx.ch"
|
data/Releases
CHANGED
data/TODO
CHANGED
@@ -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
|
+
:backtrace => 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
|
+
:exec => true
|
14
|
+
}
|
15
|
+
|
16
|
+
Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'), options)
|
@@ -0,0 +1,12 @@
|
|
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
|
+
Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'))
|
@@ -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
|
+
:ontop => true
|
14
|
+
}
|
15
|
+
|
16
|
+
Daemons.run(File.join(File.split(__FILE__)[0], 'myserver.rb'), options)
|
@@ -0,0 +1,12 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
|
4
|
+
# This is myserver.rb, an example server that is to be controlled by daemons
|
5
|
+
# and that does nothing really useful at the moment.
|
6
|
+
#
|
7
|
+
# Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
|
8
|
+
|
9
|
+
loop do
|
10
|
+
puts 'ping from myserver.rb!'
|
11
|
+
sleep(3)
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# This is myserver.rb, an example server that is to be controlled by daemons
|
2
|
+
# and that does nothing really useful at the moment.
|
3
|
+
#
|
4
|
+
# Don't run this script by yourself, it can be controlled by the ctrl*.rb scripts.
|
5
|
+
|
6
|
+
loop do
|
7
|
+
puts 'ping from myserver.rb!'
|
8
|
+
puts 'this example server will crash in 3 seconds...'
|
9
|
+
|
10
|
+
sleep(3)
|
11
|
+
|
12
|
+
puts 'CRASH!'
|
13
|
+
raise 'CRASH!'
|
14
|
+
end
|
data/lib/daemons.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
require 'optparse'
|
2
2
|
require 'optparse/time'
|
3
|
-
#require 'ostruct'
|
4
3
|
|
5
4
|
require 'daemons/pidfile'
|
6
5
|
require 'daemons/cmdline'
|
@@ -18,7 +17,7 @@ require 'daemons/exceptions'
|
|
18
17
|
#
|
19
18
|
module Daemons
|
20
19
|
|
21
|
-
VERSION = "0.0
|
20
|
+
VERSION = "0.2.0"
|
22
21
|
|
23
22
|
require 'daemons/daemonize'
|
24
23
|
|
@@ -28,8 +27,12 @@ module Daemons
|
|
28
27
|
attr_accessor :app_argv
|
29
28
|
attr_accessor :controller_argv
|
30
29
|
|
30
|
+
# the PidFile instance belonging to this application
|
31
31
|
attr_reader :pid_file
|
32
32
|
|
33
|
+
# the ApplicationGroup the application belongs to
|
34
|
+
attr_reader :group
|
35
|
+
|
33
36
|
|
34
37
|
def initialize(group, pid_file = nil)
|
35
38
|
@group = group
|
@@ -46,42 +49,145 @@ module Daemons
|
|
46
49
|
end
|
47
50
|
|
48
51
|
def start
|
49
|
-
|
52
|
+
opts = @group.controller.options
|
50
53
|
|
51
|
-
unless
|
54
|
+
unless opts[:ontop]
|
52
55
|
Daemonize.daemonize()
|
53
56
|
end
|
54
57
|
|
55
58
|
@pid_file.write
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
60
|
+
if opts[:exec]
|
61
|
+
run_via_exec()
|
62
|
+
else
|
63
|
+
# We need this to remove the pid-file if the applications exits by itself.
|
64
|
+
# Note that <tt>at_text</tt> will only be run if the applications exits by calling
|
65
|
+
# <tt>exit</tt>, and not if it calls <tt>exit!</tt>.
|
66
|
+
#
|
67
|
+
at_exit {
|
68
|
+
@pid_file.remove rescue nil
|
69
|
+
|
70
|
+
# If the option <tt>:backtrace</tt> is used and the application did exit by itself
|
71
|
+
# create a exception log.
|
72
|
+
if opts[:backtrace] and not opts[:ontop] and not $daemons_sigterm
|
73
|
+
exception_log() rescue nil
|
74
|
+
end
|
75
|
+
|
76
|
+
}
|
77
|
+
|
78
|
+
# This part is needed to remove the pid-file if the application is killed by
|
79
|
+
# daemons or manually by the user.
|
80
|
+
# Note that the applications is not supposed to overwrite the signal handler for
|
81
|
+
# 'TERM'.
|
82
|
+
#
|
83
|
+
trap('TERM') {
|
84
|
+
@pid_file.remove rescue nil
|
85
|
+
$daemons_sigterm = true
|
86
|
+
|
87
|
+
exit
|
88
|
+
}
|
89
|
+
|
90
|
+
run_via_load()
|
91
|
+
end
|
67
92
|
end
|
68
93
|
|
69
94
|
def run
|
95
|
+
if @group.controller.options[:exec]
|
96
|
+
run_via_exec()
|
97
|
+
else
|
98
|
+
run_via_load()
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def run_via_exec
|
103
|
+
ENV['DAEMONS_ARGV'] = @controller_argv.join(' ') # haven't tested yet if this is really passed to the exec'd process...
|
104
|
+
|
105
|
+
Kernel.exec(script(), *ARGV)
|
106
|
+
end
|
107
|
+
|
108
|
+
def run_via_load
|
70
109
|
$DAEMONS_ARGV = @controller_argv
|
110
|
+
ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
|
71
111
|
|
72
112
|
ARGV.clear
|
73
113
|
ARGV.concat @app_argv if @app_argv
|
74
114
|
|
115
|
+
# TODO: begin - rescue - end around this and exception logging
|
75
116
|
load script()
|
76
117
|
end
|
77
118
|
|
119
|
+
# This is a nice little function for debugging purposes:
|
120
|
+
# In case a multi-threaded ruby script exits due to an uncaught exception
|
121
|
+
# it may be difficult to find out where the exception came from because
|
122
|
+
# one cannot catch exceptions that are thrown in threads other than the main
|
123
|
+
# thread.
|
124
|
+
#
|
125
|
+
# This function searches for all exceptions in memory and outputs them to STDERR
|
126
|
+
# (if it is connected) and to a log file in the pid-file directory.
|
127
|
+
#
|
128
|
+
def exception_log
|
129
|
+
require 'logger'
|
130
|
+
|
131
|
+
l_file = Logger.new(File.join(pidfile_dir(), @group.app_name + '.log'))
|
132
|
+
|
133
|
+
|
134
|
+
# the code below only logs the last exception
|
135
|
+
e = nil
|
136
|
+
|
137
|
+
ObjectSpace.each_object {|o|
|
138
|
+
if ::Exception === o
|
139
|
+
e = o
|
140
|
+
end
|
141
|
+
}
|
142
|
+
|
143
|
+
l_file.error e
|
144
|
+
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
|
+
end
|
157
|
+
|
158
|
+
|
78
159
|
def stop
|
79
160
|
Process.kill('TERM', @pid_file.read)
|
80
161
|
|
81
|
-
#
|
82
|
-
#
|
83
|
-
|
84
|
-
|
162
|
+
# We try to remove the pid-files by ourselves, in case the application
|
163
|
+
# didn't clean it up.
|
164
|
+
@pid_file.remove rescue nil
|
165
|
+
|
166
|
+
end
|
167
|
+
|
168
|
+
def zap
|
169
|
+
@pid_file.remove
|
170
|
+
end
|
171
|
+
|
172
|
+
def show_status
|
173
|
+
running = self.running?
|
174
|
+
|
175
|
+
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
|
+
end
|
177
|
+
|
178
|
+
# This function implements a (probably too simle) method to detect
|
179
|
+
# whether the program with the pid found in the pid-file is still running.
|
180
|
+
# It just searches for the pid in the output of <tt>ps ax</tt>, which
|
181
|
+
# is probably not a good idea in some cases.
|
182
|
+
# Alternatives would be to use a direct access method the unix process control
|
183
|
+
# system.
|
184
|
+
#
|
185
|
+
def running?
|
186
|
+
if @pid_file.exists?
|
187
|
+
return / #{@pid_file.read} / =~ `ps ax`
|
188
|
+
end
|
189
|
+
|
190
|
+
return false
|
85
191
|
end
|
86
192
|
end
|
87
193
|
|
@@ -93,11 +199,6 @@ module Daemons
|
|
93
199
|
|
94
200
|
attr_reader :controller
|
95
201
|
|
96
|
-
# True if one can start multiple instances of the application
|
97
|
-
#attr_reader :multiple
|
98
|
-
|
99
|
-
#attr_reader :options
|
100
|
-
|
101
202
|
attr_reader :applications
|
102
203
|
|
103
204
|
attr_accessor :controller_argv
|
@@ -106,8 +207,10 @@ module Daemons
|
|
106
207
|
attr_accessor :dir_mode
|
107
208
|
attr_accessor :dir
|
108
209
|
|
210
|
+
# true if the application is supposed to run in multiple instances
|
109
211
|
attr_reader :multiple
|
110
212
|
|
213
|
+
|
111
214
|
def initialize(app_name, script, controller) #multiple = false)
|
112
215
|
@app_name = app_name
|
113
216
|
@script = script
|
@@ -120,8 +223,6 @@ module Daemons
|
|
120
223
|
@dir_mode = options[:dir_mode] || :script
|
121
224
|
@dir = options[:dir] || ''
|
122
225
|
|
123
|
-
#@multiple = multiple
|
124
|
-
|
125
226
|
@applications = find_applications(pidfile_dir())
|
126
227
|
end
|
127
228
|
|
@@ -159,10 +260,18 @@ module Daemons
|
|
159
260
|
def stop_all
|
160
261
|
@applications.each {|a| a.stop}
|
161
262
|
end
|
263
|
+
|
264
|
+
def zap_all
|
265
|
+
@applications.each {|a| a.zap}
|
266
|
+
end
|
267
|
+
|
268
|
+
def show_status
|
269
|
+
@applications.each {|a| a.show_status}
|
270
|
+
end
|
271
|
+
|
162
272
|
end
|
163
273
|
|
164
274
|
|
165
|
-
|
166
275
|
class Controller
|
167
276
|
|
168
277
|
attr_reader :app_name
|
@@ -173,7 +282,9 @@ module Daemons
|
|
173
282
|
'start',
|
174
283
|
'stop',
|
175
284
|
'restart',
|
176
|
-
'run'
|
285
|
+
'run',
|
286
|
+
'zap',
|
287
|
+
'status'
|
177
288
|
]
|
178
289
|
|
179
290
|
def initialize(script, argv = [])
|
@@ -190,6 +301,11 @@ module Daemons
|
|
190
301
|
end
|
191
302
|
|
192
303
|
|
304
|
+
# This function is used to do a final update of the options passed to the application
|
305
|
+
# before they are really used.
|
306
|
+
#
|
307
|
+
# Note that this function should only update <tt>@options</tt> and no other variables.
|
308
|
+
#
|
193
309
|
def setup_options
|
194
310
|
#@options[:ontop] ||= true
|
195
311
|
end
|
@@ -218,6 +334,14 @@ module Daemons
|
|
218
334
|
@group.stop_all
|
219
335
|
sleep 1
|
220
336
|
@group.start_all
|
337
|
+
when 'zap'
|
338
|
+
@group.zap_all
|
339
|
+
when 'status'
|
340
|
+
unless @group.applications.empty?
|
341
|
+
@group.show_status
|
342
|
+
else
|
343
|
+
puts "#{@group.app_name}: no instances running"
|
344
|
+
end
|
221
345
|
when nil
|
222
346
|
raise CmdException.new('no command given')
|
223
347
|
#puts "ERROR: No command given"; puts
|
@@ -225,7 +349,7 @@ module Daemons
|
|
225
349
|
#print_usage()
|
226
350
|
#raise('usage function not implemented')
|
227
351
|
else
|
228
|
-
raise Error.new("
|
352
|
+
raise Error.new("command '#{@command}' not implemented")
|
229
353
|
end
|
230
354
|
end
|
231
355
|
|
@@ -282,6 +406,12 @@ module Daemons
|
|
282
406
|
# <tt>:dir</tt>:: Used in combination with <tt>:dir_mode</tt> (description above)
|
283
407
|
# <tt>:multiple</tt>:: Specifies whether multiple instances of the same script are allowed to run at the
|
284
408
|
# same time
|
409
|
+
# <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
|
410
|
+
# (but the pid-file and other things are written as usual)
|
411
|
+
# <tt>:exec</tt>:: When given, do not start the application by <tt>load</tt>-ing the script file,
|
412
|
+
# but by exec'ing the script file
|
413
|
+
# <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
|
414
|
+
# pid-file directory if the application exits due to an uncaught exception
|
285
415
|
#
|
286
416
|
# -----
|
287
417
|
#
|
@@ -289,7 +419,10 @@ module Daemons
|
|
289
419
|
# options = {
|
290
420
|
# :dir_mode => :script,
|
291
421
|
# :dir => 'pids',
|
292
|
-
# :multiple => true
|
422
|
+
# :multiple => true,
|
423
|
+
# :ontop => true,
|
424
|
+
# :exec => true,
|
425
|
+
# :backtrace => true
|
293
426
|
# }
|
294
427
|
#
|
295
428
|
# Daemons.run(File.join(File.split(__FILE__)[0], 'myscript.rb'), options)
|
data/lib/daemons/cmdline.rb
CHANGED
@@ -78,6 +78,7 @@ module Daemons
|
|
78
78
|
puts " stop stop all instances of the application"
|
79
79
|
puts " restart stop all instances and restart them afterwards"
|
80
80
|
puts " run start the application and stay on top"
|
81
|
+
puts " zap set the application to a stopped state"
|
81
82
|
puts
|
82
83
|
puts "* and where <options> may contain several of the following:"
|
83
84
|
|
data/lib/daemons/daemonize.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
|
+
#--
|
1
2
|
###############################################################################
|
2
3
|
# daemonize.rb is a slightly modified version of daemonize.rb was #
|
3
4
|
# from the Daemonize Library written by Travis Whitton #
|
4
5
|
# for details, read the notice below #
|
5
6
|
###############################################################################
|
6
|
-
|
7
|
-
|
7
|
+
#++
|
8
|
+
#
|
9
|
+
#
|
8
10
|
# =Daemonize Library
|
9
11
|
#
|
10
12
|
# February. 4, 2005 Travis Whitton <whitton@atlantic.net>
|
@@ -132,7 +134,9 @@ module Daemonize
|
|
132
134
|
# Make sure all file descriptors are closed
|
133
135
|
ObjectSpace.each_object(IO) do |io|
|
134
136
|
unless [STDIN, STDOUT, STDERR].include?(io)
|
135
|
-
io.
|
137
|
+
unless io.closed?
|
138
|
+
io.close rescue nil
|
139
|
+
end
|
136
140
|
end
|
137
141
|
end
|
138
142
|
|
metadata
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.8.
|
2
|
+
rubygems_version: 0.8.8
|
3
3
|
specification_version: 1
|
4
4
|
name: daemons
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0
|
7
|
-
date: 2005-
|
6
|
+
version: 0.2.0
|
7
|
+
date: 2005-03-21
|
8
8
|
summary: A toolkit to convert your script to a controllable daemon
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -41,6 +41,12 @@ files:
|
|
41
41
|
- test/tc_main.rb
|
42
42
|
- test/test1.rb
|
43
43
|
- test/tmp
|
44
|
+
- examples/myserver.rb
|
45
|
+
- examples/myserver_crashing.rb
|
46
|
+
- examples/ctrl_crash.rb
|
47
|
+
- examples/ctrl_ontop.rb
|
48
|
+
- examples/ctrl_exec.rb
|
49
|
+
- examples/ctrl_normal.rb
|
44
50
|
test_files:
|
45
51
|
- test/tc_main.rb
|
46
52
|
rdoc_options: []
|