mwotton-daemons 1.0.11

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,22 @@
1
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
2
+
3
+ if File.exist?(File.join(lib_dir, 'daemons.rb'))
4
+ $LOAD_PATH.unshift lib_dir
5
+ else
6
+ begin; require 'rubygems'; rescue ::Exception; end
7
+ end
8
+
9
+ require 'daemons'
10
+
11
+
12
+ options = {
13
+ :log_output => true,
14
+ :multiple => true,
15
+ }
16
+
17
+
18
+ Daemons.run_proc('ctrl_proc_multiple.rb', options) do
19
+ puts "hello"
20
+ sleep(5)
21
+ puts "done"
22
+ end
@@ -0,0 +1,17 @@
1
+ lib_dir = File.expand_path(File.join(File.dirname(__FILE__), '../../lib'))
2
+
3
+ if File.exist?(File.join(lib_dir, 'daemons.rb'))
4
+ $LOAD_PATH.unshift lib_dir
5
+ else
6
+ begin; require 'rubygems'; rescue ::Exception; end
7
+ end
8
+
9
+ require 'daemons'
10
+
11
+
12
+ Daemons.run_proc('ctrl_proc_simple.rb') do
13
+ loop do
14
+ puts 'ping from proc!'
15
+ sleep(3)
16
+ end
17
+ end
@@ -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
@@ -0,0 +1,30 @@
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!
16
+ ping from myserver.rb!
17
+ this example server will crash in 3 seconds...
18
+ CRASH!
19
+ /Users/uehli/Projects/daemons-proj/examples/run/myserver_crashing.rb:13: CRASH! (RuntimeError)
20
+ from /Users/uehli/Projects/daemons-proj/examples/run/myserver_crashing.rb:6:in `loop'
21
+ from /Users/uehli/Projects/daemons-proj/examples/run/myserver_crashing.rb:6
22
+ from /Users/uehli/Projects/daemons-proj/lib/daemons/application.rb:176:in `load'
23
+ from /Users/uehli/Projects/daemons-proj/lib/daemons/application.rb:176:in `start_load'
24
+ from /Users/uehli/Projects/daemons-proj/lib/daemons/application.rb:257:in `start'
25
+ from /Users/uehli/Projects/daemons-proj/lib/daemons/controller.rb:69:in `run'
26
+ from /Users/uehli/Projects/daemons-proj/lib/daemons.rb:139:in `run'
27
+ from /Users/uehli/Projects/daemons-proj/lib/daemons/cmdline.rb:105:in `call'
28
+ from /Users/uehli/Projects/daemons-proj/lib/daemons/cmdline.rb:105:in `catch_exceptions'
29
+ from /Users/uehli/Projects/daemons-proj/lib/daemons.rb:138:in `run'
30
+ from ctrl_crash.rb:17
@@ -0,0 +1,8 @@
1
+ loop do
2
+ puts 'ping from myserver.rb!'
3
+ puts 'this example server will exit in 3 seconds...'
4
+
5
+ sleep(3)
6
+
7
+ Process.exit
8
+ end
@@ -0,0 +1,283 @@
1
+ require 'optparse'
2
+ require 'optparse/time'
3
+
4
+
5
+ require 'daemons/pidfile'
6
+ require 'daemons/cmdline'
7
+ require 'daemons/exceptions'
8
+ require 'daemons/monitor'
9
+
10
+
11
+ require 'daemons/application'
12
+ require 'daemons/application_group'
13
+ require 'daemons/controller'
14
+
15
+
16
+ # All functions and classes that Daemons provides reside in this module.
17
+ #
18
+ # Daemons is normally invoked by one of the following four ways:
19
+ #
20
+ # 1. <tt>Daemons.run(script, options)</tt>:
21
+ # This is used in wrapper-scripts that are supposed to control other ruby scripts or
22
+ # external applications. Control is completely passed to the daemons library.
23
+ # Such wrapper script need to be invoked with command line options like 'start' or 'stop'
24
+ # to do anything useful.
25
+ #
26
+ # 2. <tt>Daemons.run_proc(app_name, options) { (...) }</tt>:
27
+ # This is used in wrapper-scripts that are supposed to control a proc.
28
+ # Control is completely passed to the daemons library.
29
+ # Such wrapper script need to be invoked with command line options like 'start' or 'stop'
30
+ # to do anything useful.
31
+ #
32
+ # 3. <tt>Daemons.call(options) { block }</tt>:
33
+ # Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
34
+ # after spawning the daemon with the new Application object as a return value.
35
+ #
36
+ # 4. <tt>Daemons.daemonize(options)</tt>:
37
+ # Daemonize the currently runnig process, i.e. the calling process will become a daemon.
38
+ #
39
+ # == What does daemons internally do with my daemons?
40
+ # *or*:: why do my daemons crash when they try to open a file?
41
+ # *or*:: why can I not see any output from the daemon on the console (when using for example +puts+)?
42
+ #
43
+ # From a technical aspect of view, daemons does the following when creating a daemon:
44
+ #
45
+ # 1. Forks a child (and exits the parent process, if needed)
46
+ # 2. Becomes a session leader (which detaches the program from
47
+ # the controlling terminal).
48
+ # 3. Forks another child process and exits first child. This prevents
49
+ # the potential of acquiring a controlling terminal.
50
+ # 4. Changes the current working directory to "/".
51
+ # 5. Clears the file creation mask (sets +umask+ to 0000).
52
+ # 6. Closes file descriptors (reopens +STDOUT+ and +STDERR+ to point to a logfile if
53
+ # possible).
54
+ #
55
+ # So what does this mean for your daemons:
56
+ # - the current directory is '/'
57
+ # - you cannot receive any input from the console (for example no +gets+)
58
+ # - you cannot output anything from the daemons with +puts+/+print+ unless a logfile is used
59
+ #
60
+ # == How do PidFiles work? Where are they stored?
61
+ #
62
+ # Also, you are maybe interested in reading the documentation for the class PidFile.
63
+ # There you can find out about how Daemons works internally and how and where the so
64
+ # called <i>PidFiles</i> are stored.
65
+ #
66
+ module Daemons
67
+
68
+ VERSION = "1.0.11"
69
+
70
+ require 'daemons/daemonize'
71
+
72
+
73
+ # Passes control to Daemons.
74
+ # This is used in wrapper-scripts that are supposed to control other ruby scripts or
75
+ # external applications. Control is completely passed to the daemons library.
76
+ # Such wrapper script should be invoked with command line options like 'start' or 'stop'
77
+ # to do anything useful.
78
+ #
79
+ # +script+:: This is the path to the script that should be run as a daemon.
80
+ # Please note that Daemons runs this script with <tt>load <script></tt>.
81
+ # Also note that Daemons cannot detect the directory in which the controlling
82
+ # script resides, so this has to be either an absolute path or you have to run
83
+ # the controlling script from the appropriate directory.
84
+ #
85
+ # +options+:: A hash that may contain one or more of the options listed below
86
+ #
87
+ # === Options:
88
+ # <tt>:app_name</tt>:: The name of the application. This will be
89
+ # used to contruct the name of the pid files
90
+ # and log files. Defaults to the basename of
91
+ # the script.
92
+ # <tt>:ARGV</tt>:: An array of strings containing parameters and switches for Daemons.
93
+ # This includes both parameters for Daemons itself and the controlled scripted.
94
+ # These are assumed to be separated by an array element '--', .e.g.
95
+ # ['start', 'f', '--', 'param1_for_script', 'param2_for_script'].
96
+ # If not given, ARGV (the parameters given to the Ruby process) will be used.
97
+ # <tt>:dir_mode</tt>:: Either <tt>:script</tt> (the directory for writing the pid files to
98
+ # given by <tt>:dir</tt> is interpreted relative
99
+ # to the script location given by +script+) or <tt>:normal</tt> (the directory given by
100
+ # <tt>:dir</tt> is interpreted as a (absolute or relative) path) or <tt>:system</tt>
101
+ # (<tt>/var/run</tt> is used as the pid file directory)
102
+ #
103
+ # <tt>:dir</tt>:: Used in combination with <tt>:dir_mode</tt> (description above)
104
+ # <tt>:multiple</tt>:: Specifies whether multiple instances of the same script are allowed to run at the
105
+ # same time
106
+ # <tt>:ontop</tt>:: When given (i.e. set to true), stay on top, i.e. do not daemonize the application
107
+ # (but the pid-file and other things are written as usual)
108
+ # <tt>:mode</tt>:: <tt>:load</tt> Load the script with <tt>Kernel.load</tt>;
109
+ # <tt>:exec</tt> Execute the script file with <tt>Kernel.exec</tt>
110
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
111
+ # pid-file directory if the application exits due to an uncaught exception
112
+ # <tt>:monitor</tt>:: Monitor the programs and restart crashed instances
113
+ # <tt>:log_output</tt>:: When given (i.e. set to true), redirect both STDOUT and STDERR to a logfile named '[app_name].output' in the pid-file directory
114
+ # <tt>:keep_pid_files</tt>:: When given do not delete lingering pid-files (files for which the process is no longer running).
115
+ # <tt>:hard_exit</tt>:: When given use exit! to end a daemons instead of exit (this will for example
116
+ # not call at_exit handlers).
117
+ # -----
118
+ #
119
+ # === Example:
120
+ # options = {
121
+ # :app_name => "my_app",
122
+ # :ARGV => ['start', '-f', '--', 'param_for_myscript']
123
+ # :dir_mode => :script,
124
+ # :dir => 'pids',
125
+ # :multiple => true,
126
+ # :ontop => true,
127
+ # :mode => :exec,
128
+ # :backtrace => true,
129
+ # :monitor => true
130
+ # }
131
+ #
132
+ # Daemons.run(File.join(File.dirname(__FILE__), 'myscript.rb'), options)
133
+ #
134
+ def run(script, options = {})
135
+ options[:script] = script
136
+ @controller = Controller.new(options, options[:ARGV] || ARGV)
137
+
138
+ @controller.catch_exceptions {
139
+ @controller.run
140
+ }
141
+
142
+ # I don't think anybody will ever use @group, as this location should not be reached under non-error conditions
143
+ @group = @controller.group
144
+ end
145
+ module_function :run
146
+
147
+
148
+ # Passes control to Daemons.
149
+ # This function does the same as Daemons.run except that not a script but a proc
150
+ # will be run as a daemon while this script provides command line options like 'start' or 'stop'
151
+ # and the whole pid-file management to control the proc.
152
+ #
153
+ # +app_name+:: The name of the application. This will be
154
+ # used to contruct the name of the pid files
155
+ # and log files. Defaults to the basename of
156
+ # the script.
157
+ #
158
+ # +options+:: A hash that may contain one or more of the options listed in the documentation for Daemons.run
159
+ #
160
+ # A block must be given to this function. The block will be used as the :proc entry in the options hash.
161
+ # -----
162
+ #
163
+ # === Example:
164
+ #
165
+ # Daemons.run_proc('myproc.rb') do
166
+ # loop do
167
+ # accept_connection()
168
+ # read_request()
169
+ # send_response()
170
+ # close_connection()
171
+ # end
172
+ # end
173
+ #
174
+ def run_proc(app_name, options = {}, &block)
175
+ options[:app_name] = app_name
176
+ options[:mode] = :proc
177
+ options[:proc] = block
178
+
179
+ # we do not have a script location so the the :script :dir_mode cannot be used, change it to :normal
180
+ if [nil, :script].include? options[:dir_mode]
181
+ options[:dir_mode] = :normal
182
+ options[:dir] = File.expand_path('.')
183
+ end
184
+
185
+ @controller = Controller.new(options, options[:ARGV] || ARGV)
186
+
187
+ @controller.catch_exceptions {
188
+ @controller.run
189
+ }
190
+
191
+ # I don't think anybody will ever use @group, as this location should not be reached under non-error conditions
192
+ @group = @controller.group
193
+ end
194
+ module_function :run_proc
195
+
196
+
197
+ # Execute the block in a new daemon. <tt>Daemons.call</tt> will return immediately
198
+ # after spawning the daemon with the new Application object as a return value.
199
+ #
200
+ # +options+:: A hash that may contain one or more of the options listed below
201
+ #
202
+ # +block+:: The block to call in the daemon.
203
+ #
204
+ # === Options:
205
+ # <tt>:multiple</tt>:: Specifies whether multiple instances of the same script are allowed to run at the
206
+ # same time
207
+ # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
208
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
209
+ # pid-file directory if the application exits due to an uncaught exception
210
+ # -----
211
+ #
212
+ # === Example:
213
+ # options = {
214
+ # :backtrace => true,
215
+ # :monitor => true,
216
+ # :ontop => true
217
+ # }
218
+ #
219
+ # Daemons.call(options) begin
220
+ # # Server loop:
221
+ # loop {
222
+ # conn = accept_conn()
223
+ # serve(conn)
224
+ # }
225
+ # end
226
+ #
227
+ def call(options = {}, &block)
228
+ unless block_given?
229
+ raise "Daemons.call: no block given"
230
+ end
231
+
232
+ options[:proc] = block
233
+ options[:mode] = :proc
234
+
235
+ @group ||= ApplicationGroup.new('proc', options)
236
+
237
+ new_app = @group.new_application(options)
238
+ new_app.start
239
+
240
+ return new_app
241
+ end
242
+ module_function :call
243
+
244
+
245
+ # Daemonize the currently runnig process, i.e. the calling process will become a daemon.
246
+ #
247
+ # +options+:: A hash that may contain one or more of the options listed below
248
+ #
249
+ # === Options:
250
+ # <tt>:ontop</tt>:: When given, stay on top, i.e. do not daemonize the application
251
+ # <tt>:backtrace</tt>:: Write a backtrace of the last exceptions to the file '[app_name].log' in the
252
+ # pid-file directory if the application exits due to an uncaught exception
253
+ # -----
254
+ #
255
+ # === Example:
256
+ # options = {
257
+ # :backtrace => true,
258
+ # :ontop => true
259
+ # }
260
+ #
261
+ # Daemons.daemonize(options)
262
+ #
263
+ # # Server loop:
264
+ # loop {
265
+ # conn = accept_conn()
266
+ # serve(conn)
267
+ # }
268
+ #
269
+ def daemonize(options = {})
270
+ @group ||= ApplicationGroup.new('self', options)
271
+
272
+ @group.new_application(:mode => :none).start
273
+ end
274
+ module_function :daemonize
275
+
276
+ # Return the internal ApplicationGroup instance.
277
+ def group; @group; end
278
+ module_function :group
279
+
280
+ # Return the internal Controller instance.
281
+ def controller; @controller; end
282
+ module_function :controller
283
+ end
@@ -0,0 +1,376 @@
1
+ require 'daemons/pidfile'
2
+ require 'daemons/pidmem'
3
+
4
+
5
+ module Daemons
6
+
7
+ class Application
8
+
9
+ attr_accessor :app_argv
10
+ attr_accessor :controller_argv
11
+
12
+ # the Pid instance belonging to this application
13
+ attr_reader :pid
14
+
15
+ # the ApplicationGroup the application belongs to
16
+ attr_reader :group
17
+
18
+ # my private options
19
+ attr_reader :options
20
+
21
+
22
+ SIGNAL = (RUBY_PLATFORM =~ /win32/ ? 'KILL' : 'TERM')
23
+
24
+
25
+ def initialize(group, add_options = {}, pid = nil)
26
+ @group = group
27
+ @options = group.options.dup
28
+ @options.update(add_options)
29
+
30
+ @dir_mode = @dir = @script = nil
31
+
32
+ unless @pid = pid
33
+ if dir = pidfile_dir
34
+ @pid = PidFile.new(dir, @group.app_name, @group.multiple)
35
+ else
36
+ @pid = PidMem.new
37
+ end
38
+ end
39
+ end
40
+
41
+ def script
42
+ @script || @group.script
43
+ end
44
+
45
+ def pidfile_dir
46
+ Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
47
+ end
48
+
49
+ def output_logfile
50
+ logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
51
+ (options[:log_output] && logdir) ? File.join(logdir, @group.app_name + '.output') : nil
52
+ end
53
+
54
+ def logfile
55
+ logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
56
+ logdir ? File.join(logdir, @group.app_name + '.log') : nil
57
+ end
58
+
59
+ # this function is only used to daemonize the currently running process (Daemons.daemonize)
60
+ def start_none
61
+ unless options[:ontop]
62
+ Daemonize.daemonize(nil, @group.app_name) #(logfile)
63
+ else
64
+ Daemonize.simulate
65
+ end
66
+
67
+ @pid.pid = Process.pid
68
+
69
+
70
+ # We need this to remove the pid-file if the applications exits by itself.
71
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
72
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
73
+ # in your application!
74
+ #
75
+ at_exit {
76
+ begin; @pid.cleanup; rescue ::Exception; end
77
+
78
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
79
+ # create a exception log.
80
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
81
+ begin; exception_log(); rescue ::Exception; end
82
+ end
83
+
84
+ }
85
+
86
+ # This part is needed to remove the pid-file if the application is killed by
87
+ # daemons or manually by the user.
88
+ # Note that the applications is not supposed to overwrite the signal handler for
89
+ # 'TERM'.
90
+ #
91
+ trap(SIGNAL) {
92
+ begin; @pid.cleanup; rescue ::Exception; end
93
+ $daemons_sigterm = true
94
+
95
+ if options[:hard_exit]
96
+ exit!
97
+ else
98
+ exit
99
+ end
100
+ }
101
+ end
102
+
103
+ def start_exec
104
+ if options[:backtrace]
105
+ puts "option :backtrace is not supported with :mode => :exec, ignoring"
106
+ end
107
+
108
+ unless options[:ontop]
109
+ Daemonize.daemonize(output_logfile, @group.app_name)
110
+ else
111
+ Daemonize.simulate(output_logfile)
112
+ end
113
+
114
+ # note that we cannot remove the pid file if we run in :ontop mode (i.e. 'ruby ctrl_exec.rb run')
115
+ @pid.pid = Process.pid
116
+
117
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
118
+ # haven't tested yet if this is really passed to the exec'd process...
119
+
120
+
121
+
122
+ Kernel.exec(script(), *(@app_argv || []))
123
+ #Kernel.exec(script(), *ARGV)
124
+ end
125
+
126
+ def start_load
127
+ unless options[:ontop]
128
+ Daemonize.daemonize(output_logfile, @group.app_name)
129
+ else
130
+ Daemonize.simulate(output_logfile)
131
+ end
132
+
133
+ @pid.pid = Process.pid
134
+
135
+
136
+ # We need this to remove the pid-file if the applications exits by itself.
137
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
138
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
139
+ # in your application!
140
+ #
141
+ at_exit {
142
+ begin; @pid.cleanup; rescue ::Exception; end
143
+
144
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
145
+ # create a exception log.
146
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
147
+ begin; exception_log(); rescue ::Exception; end
148
+ end
149
+
150
+ }
151
+
152
+ # This part is needed to remove the pid-file if the application is killed by
153
+ # daemons or manually by the user.
154
+ # Note that the applications is not supposed to overwrite the signal handler for
155
+ # 'TERM'.
156
+ #
157
+ trap(SIGNAL) {
158
+ begin; @pid.cleanup; rescue ::Exception; end
159
+ $daemons_sigterm = true
160
+
161
+ if options[:hard_exit]
162
+ exit!
163
+ else
164
+ exit
165
+ end
166
+ }
167
+
168
+ # Now we really start the script...
169
+ $DAEMONS_ARGV = @controller_argv
170
+ ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
171
+
172
+ ARGV.clear
173
+ ARGV.concat @app_argv if @app_argv
174
+
175
+ # TODO: begin - rescue - end around this and exception logging
176
+ load script()
177
+ end
178
+
179
+ def start_proc
180
+ return unless p = options[:proc]
181
+
182
+ myproc = proc do
183
+ # We need this to remove the pid-file if the applications exits by itself.
184
+ # Note that <tt>at_text</tt> will only be run if the applications exits by calling
185
+ # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
186
+ # in your application!
187
+ #
188
+ at_exit {
189
+ begin; @pid.cleanup; rescue ::Exception; end
190
+
191
+ # If the option <tt>:backtrace</tt> is used and the application did exit by itself
192
+ # create a exception log.
193
+ if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
194
+ begin; exception_log(); rescue ::Exception; end
195
+ end
196
+
197
+ }
198
+
199
+ # This part is needed to remove the pid-file if the application is killed by
200
+ # daemons or manually by the user.
201
+ # Note that the applications is not supposed to overwrite the signal handler for
202
+ # 'TERM'.
203
+ #
204
+ trap(SIGNAL) {
205
+ begin; @pid.cleanup; rescue ::Exception; end
206
+ $daemons_sigterm = true
207
+
208
+ if options[:hard_exit]
209
+ exit!
210
+ else
211
+ exit
212
+ end
213
+ }
214
+
215
+ p.call()
216
+ end
217
+
218
+ unless options[:ontop]
219
+ @pid.pid = Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name)
220
+ else
221
+ Daemonize.simulate(output_logfile)
222
+
223
+ @pid.pid = Process.pid
224
+
225
+ myproc.call
226
+
227
+ # why did we use this??
228
+ # Thread.new(&options[:proc])
229
+
230
+ # why did we use the code below??
231
+ # unless pid = Process.fork
232
+ # @pid.pid = pid
233
+ # Daemonize.simulate(logfile)
234
+ # options[:proc].call
235
+ # exit
236
+ # else
237
+ # Process.detach(@pid.pid)
238
+ # end
239
+ end
240
+ end
241
+
242
+
243
+ def start
244
+ @group.create_monitor(@group.applications[0] || self) unless options[:ontop] # we don't monitor applications in the foreground
245
+
246
+ case options[:mode]
247
+ when :none
248
+ # this is only used to daemonize the currently running process
249
+ start_none
250
+ when :exec
251
+ start_exec
252
+ when :load
253
+ start_load
254
+ when :proc
255
+ start_proc
256
+ else
257
+ start_load
258
+ end
259
+ end
260
+
261
+ # def run
262
+ # if @group.controller.options[:exec]
263
+ # run_via_exec()
264
+ # else
265
+ # run_via_load()
266
+ # end
267
+ # end
268
+ #
269
+ # def run_via_exec
270
+ #
271
+ # end
272
+ #
273
+ # def run_via_load
274
+ #
275
+ # end
276
+
277
+
278
+ # This is a nice little function for debugging purposes:
279
+ # In case a multi-threaded ruby script exits due to an uncaught exception
280
+ # it may be difficult to find out where the exception came from because
281
+ # one cannot catch exceptions that are thrown in threads other than the main
282
+ # thread.
283
+ #
284
+ # This function searches for all exceptions in memory and outputs them to STDERR
285
+ # (if it is connected) and to a log file in the pid-file directory.
286
+ #
287
+ def exception_log
288
+ return unless logfile
289
+
290
+ require 'logger'
291
+
292
+ l_file = Logger.new(logfile)
293
+
294
+ # the code below finds the last exception
295
+ e = nil
296
+
297
+ ObjectSpace.each_object {|o|
298
+ if ::Exception === o
299
+ e = o
300
+ end
301
+ }
302
+
303
+ l_file.info "*** below you find the most recent exception thrown, this will be likely (but not certainly) the exception that made the application exit abnormally ***"
304
+ l_file.error e
305
+
306
+ l_file.info "*** below you find all exception objects found in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***"
307
+
308
+ # this code logs every exception found in memory
309
+ ObjectSpace.each_object {|o|
310
+ if ::Exception === o
311
+ l_file.error o
312
+ end
313
+ }
314
+
315
+ l_file.close
316
+ end
317
+
318
+
319
+ def stop
320
+ if options[:force] and not running?
321
+ self.zap
322
+ return
323
+ end
324
+
325
+ # Catch errors when trying to kill a process that doesn't
326
+ # exist. This happens when the process quits and hasn't been
327
+ # restarted by the monitor yet. By catching the error, we allow the
328
+ # pid file clean-up to occur.
329
+ pid = @pid.pid
330
+ begin
331
+ Process.kill(SIGNAL, pid)
332
+ while Pid.running?(pid)
333
+ sleep 0.1
334
+ end
335
+ rescue Errno::ESRCH => e
336
+ puts "#{e} #{@pid.pid}"
337
+ puts "deleting pid-file."
338
+ end
339
+
340
+ # We try to remove the pid-files by ourselves, in case the application
341
+ # didn't clean it up.
342
+ begin; @pid.cleanup; rescue ::Exception; end
343
+
344
+ end
345
+
346
+ def zap
347
+ @pid.cleanup
348
+ end
349
+
350
+ def zap!
351
+ begin; @pid.cleanup; rescue ::Exception; end
352
+ end
353
+
354
+ def show_status
355
+ running = self.running?
356
+
357
+ puts "#{self.group.app_name}: #{running ? '' : 'not '}running#{(running and @pid.exist?) ? ' [pid ' + @pid.pid.to_s + ']' : ''}#{(@pid.exist? and not running) ? ' (but pid-file exists: ' + @pid.pid.to_s + ')' : ''}"
358
+ end
359
+
360
+ # This function implements a (probably too simle) method to detect
361
+ # whether the program with the pid found in the pid-file is still running.
362
+ # It just searches for the pid in the output of <tt>ps ax</tt>, which
363
+ # is probably not a good idea in some cases.
364
+ # Alternatives would be to use a direct access method the unix process control
365
+ # system.
366
+ #
367
+ def running?
368
+ if @pid.exist?
369
+ return Pid.running?(@pid.pid)
370
+ end
371
+
372
+ return false
373
+ end
374
+ end
375
+
376
+ end