fire_and_forget 0.3.2 → 0.3.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,468 +0,0 @@
1
- require 'daemons/pidfile'
2
- require 'daemons/pidmem'
3
- require 'daemons/change_privilege'
4
-
5
- require 'timeout'
6
-
7
-
8
- module Daemons
9
-
10
- class Application
11
-
12
- attr_accessor :app_argv
13
- attr_accessor :controller_argv
14
-
15
- # the Pid instance belonging to this application
16
- attr_reader :pid
17
-
18
- # the ApplicationGroup the application belongs to
19
- attr_reader :group
20
-
21
- # my private options
22
- attr_reader :options
23
-
24
-
25
- SIGNAL = (RUBY_PLATFORM =~ /win32/ ? 'KILL' : 'TERM')
26
-
27
-
28
- def initialize(group, add_options = {}, pid = nil)
29
- @group = group
30
- @options = group.options.dup
31
- @options.update(add_options)
32
-
33
- @dir_mode = @dir = @script = nil
34
-
35
- @force_kill_waittime = @options[:force_kill_waittime] || 20
36
-
37
- unless @pid = pid
38
- if @options[:no_pidfiles]
39
- @pid = PidMem.new
40
- elsif dir = pidfile_dir
41
- @pid = PidFile.new(dir, @group.app_name, @group.multiple)
42
- else
43
- @pid = PidMem.new
44
- end
45
- end
46
- end
47
-
48
- def change_privilege
49
- user = options[:user]
50
- group = options[:group]
51
- CurrentProcess.change_privilege(user, group) if user
52
- end
53
-
54
- def script
55
- @script || @group.script
56
- end
57
-
58
- def pidfile_dir
59
- Pid.dir(@dir_mode || @group.dir_mode, @dir || @group.dir, @script || @group.script)
60
- end
61
-
62
- def logdir
63
- logdir = options[:log_dir]
64
- unless logdir
65
- logdir = options[:dir_mode] == :system ? '/var/log' : pidfile_dir
66
- end
67
- logdir
68
- end
69
-
70
- def output_logfile
71
- (options[:log_output] && logdir) ? File.join(logdir, @group.app_name + '.output') : nil
72
- end
73
-
74
- def logfile
75
- logdir ? File.join(logdir, @group.app_name + '.log') : nil
76
- end
77
-
78
- # this function is only used to daemonize the currently running process (Daemons.daemonize)
79
- def start_none
80
- unless options[:ontop]
81
- Daemonize.daemonize(nil, @group.app_name) #(logfile)
82
- else
83
- Daemonize.simulate
84
- end
85
-
86
- @pid.pid = Process.pid
87
-
88
-
89
- # We need this to remove the pid-file if the applications exits by itself.
90
- # Note that <tt>at_text</tt> will only be run if the applications exits by calling
91
- # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
92
- # in your application!
93
- #
94
- at_exit {
95
- begin; @pid.cleanup; rescue ::Exception; end
96
-
97
- # If the option <tt>:backtrace</tt> is used and the application did exit by itself
98
- # create a exception log.
99
- if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
100
- begin; exception_log(); rescue ::Exception; end
101
- end
102
-
103
- }
104
-
105
- # This part is needed to remove the pid-file if the application is killed by
106
- # daemons or manually by the user.
107
- # Note that the applications is not supposed to overwrite the signal handler for
108
- # 'TERM'.
109
- #
110
- trap(SIGNAL) {
111
- begin; @pid.cleanup; rescue ::Exception; end
112
- $daemons_sigterm = true
113
-
114
- if options[:hard_exit]
115
- exit!
116
- else
117
- exit
118
- end
119
- }
120
- end
121
-
122
- def start_exec
123
- if options[:backtrace]
124
- puts "option :backtrace is not supported with :mode => :exec, ignoring"
125
- end
126
-
127
- unless options[:ontop]
128
- Daemonize.daemonize(output_logfile, @group.app_name)
129
- else
130
- Daemonize.simulate(output_logfile)
131
- end
132
-
133
- # note that we cannot remove the pid file if we run in :ontop mode (i.e. 'ruby ctrl_exec.rb run')
134
- @pid.pid = Process.pid
135
-
136
- ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
137
- # haven't tested yet if this is really passed to the exec'd process...
138
-
139
- started()
140
- Kernel.exec(script(), *(@app_argv || []))
141
- #Kernel.exec(script(), *ARGV)
142
- end
143
-
144
- def start_load
145
- unless options[:ontop]
146
- Daemonize.daemonize(output_logfile, @group.app_name)
147
- else
148
- Daemonize.simulate(output_logfile)
149
- end
150
-
151
- @pid.pid = Process.pid
152
-
153
-
154
- # We need this to remove the pid-file if the applications exits by itself.
155
- # Note that <tt>at_exit</tt> will only be run if the applications exits by calling
156
- # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
157
- # in your application!
158
- #
159
- at_exit {
160
- begin; @pid.cleanup; rescue ::Exception; end
161
-
162
- # If the option <tt>:backtrace</tt> is used and the application did exit by itself
163
- # create a exception log.
164
- if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
165
- begin; exception_log(); rescue ::Exception; end
166
- end
167
-
168
- }
169
-
170
- # This part is needed to remove the pid-file if the application is killed by
171
- # daemons or manually by the user.
172
- # Note that the applications is not supposed to overwrite the signal handler for
173
- # 'TERM'.
174
- #
175
- $daemons_stop_proc = options[:stop_proc]
176
- trap(SIGNAL) {
177
- begin
178
- if $daemons_stop_proc
179
- $daemons_stop_proc.call
180
- end
181
- rescue ::Exception
182
- end
183
-
184
- begin; @pid.cleanup; rescue ::Exception; end
185
- $daemons_sigterm = true
186
-
187
- if options[:hard_exit]
188
- exit!
189
- else
190
- exit
191
- end
192
- }
193
-
194
- # Now we really start the script...
195
- $DAEMONS_ARGV = @controller_argv
196
- ENV['DAEMONS_ARGV'] = @controller_argv.join(' ')
197
-
198
- ARGV.clear
199
- ARGV.concat @app_argv if @app_argv
200
-
201
- started()
202
- # TODO: begin - rescue - end around this and exception logging
203
- load script()
204
- end
205
-
206
- def start_proc
207
- return unless p = options[:proc]
208
-
209
- myproc = proc do
210
- # We need this to remove the pid-file if the applications exits by itself.
211
- # Note that <tt>at_text</tt> will only be run if the applications exits by calling
212
- # <tt>exit</tt>, and not if it calls <tt>exit!</tt> (so please don't call <tt>exit!</tt>
213
- # in your application!
214
- #
215
- at_exit {
216
- begin; @pid.cleanup; rescue ::Exception; end
217
-
218
- # If the option <tt>:backtrace</tt> is used and the application did exit by itself
219
- # create a exception log.
220
- if options[:backtrace] and not options[:ontop] and not $daemons_sigterm
221
- begin; exception_log(); rescue ::Exception; end
222
- end
223
-
224
- }
225
-
226
- # This part is needed to remove the pid-file if the application is killed by
227
- # daemons or manually by the user.
228
- # Note that the applications is not supposed to overwrite the signal handler for
229
- # 'TERM'.
230
- #
231
- $daemons_stop_proc = options[:stop_proc]
232
- trap(SIGNAL) {
233
- begin
234
- if $daemons_stop_proc
235
- $daemons_stop_proc.call
236
- end
237
- rescue ::Exception
238
- end
239
-
240
- begin; @pid.cleanup; rescue ::Exception; end
241
- $daemons_sigterm = true
242
-
243
- if options[:hard_exit]
244
- exit!
245
- else
246
- exit
247
- end
248
- }
249
-
250
- p.call()
251
- end
252
-
253
- unless options[:ontop]
254
- @pid.pid = Daemonize.call_as_daemon(myproc, output_logfile, @group.app_name)
255
-
256
- else
257
- Daemonize.simulate(output_logfile)
258
-
259
- @pid.pid = Process.pid
260
-
261
- myproc.call
262
-
263
- # why did we use this??
264
- # Thread.new(&options[:proc])
265
-
266
- # why did we use the code below??
267
- # unless pid = Process.fork
268
- # @pid.pid = pid
269
- # Daemonize.simulate(logfile)
270
- # options[:proc].call
271
- # exit
272
- # else
273
- # Process.detach(@pid.pid)
274
- # end
275
- end
276
-
277
- started()
278
- end
279
-
280
-
281
- def start
282
- change_privilege
283
- @group.create_monitor(@group.applications[0] || self) unless options[:ontop] # we don't monitor applications in the foreground
284
-
285
- case options[:mode]
286
- when :none
287
- # this is only used to daemonize the currently running process
288
- start_none
289
- when :exec
290
- start_exec
291
- when :load
292
- start_load
293
- when :proc
294
- start_proc
295
- else
296
- start_load
297
- end
298
- end
299
-
300
- def started
301
- if pid = @pid.pid
302
- puts "#{self.group.app_name}: process with pid #{pid} started."
303
- STDOUT.flush
304
- end
305
- end
306
-
307
-
308
- # def run
309
- # if @group.controller.options[:exec]
310
- # run_via_exec()
311
- # else
312
- # run_via_load()
313
- # end
314
- # end
315
- #
316
- # def run_via_exec
317
- #
318
- # end
319
- #
320
- # def run_via_load
321
- #
322
- # end
323
-
324
- def reload
325
- Process.kill('HUP', @pid.pid)
326
- rescue
327
- # ignore
328
- end
329
-
330
- # This is a nice little function for debugging purposes:
331
- # In case a multi-threaded ruby script exits due to an uncaught exception
332
- # it may be difficult to find out where the exception came from because
333
- # one cannot catch exceptions that are thrown in threads other than the main
334
- # thread.
335
- #
336
- # This function searches for all exceptions in memory and outputs them to STDERR
337
- # (if it is connected) and to a log file in the pid-file directory.
338
- #
339
- def exception_log
340
- return unless logfile
341
-
342
- require 'logger'
343
-
344
- l_file = Logger.new(logfile)
345
-
346
- # the code below finds the last exception
347
- e = nil
348
-
349
- ObjectSpace.each_object {|o|
350
- if ::Exception === o
351
- e = o
352
- end
353
- }
354
-
355
- 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 ***"
356
- l_file.error e
357
-
358
- 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 ***"
359
-
360
- # this code logs every exception found in memory
361
- ObjectSpace.each_object {|o|
362
- if ::Exception === o
363
- l_file.error o
364
- end
365
- }
366
-
367
- l_file.close
368
- end
369
-
370
-
371
- def stop(no_wait = false)
372
- if not running?
373
- self.zap
374
- return
375
- end
376
-
377
- pid = @pid.pid
378
-
379
- # Catch errors when trying to kill a process that doesn't
380
- # exist. This happens when the process quits and hasn't been
381
- # restarted by the monitor yet. By catching the error, we allow the
382
- # pid file clean-up to occur.
383
- begin
384
- Process.kill(SIGNAL, pid)
385
- rescue Errno::ESRCH => e
386
- puts "#{e} #{pid}"
387
- puts "deleting pid-file."
388
- end
389
-
390
- if not no_wait
391
- if @force_kill_waittime > 0
392
- puts "#{self.group.app_name}: trying to stop process with pid #{pid}..."
393
- STDOUT.flush
394
-
395
- begin
396
- Timeout::timeout(@force_kill_waittime) {
397
- while @pid.running?
398
- sleep(0.2)
399
- end
400
- }
401
- rescue Timeout::Error
402
- puts "#{self.group.app_name}: process with pid #{pid} won't stop, we forcefully kill it..."
403
- STDOUT.flush
404
-
405
- begin
406
- Process.kill('KILL', pid)
407
- rescue Errno::ESRCH
408
- end
409
-
410
- begin
411
- Timeout::timeout(20) {
412
- while @pid.running?
413
- sleep(1)
414
- end
415
- }
416
- rescue Timeout::Error
417
- puts "#{self.group.app_name}: unable to forcefully kill process with pid #{pid}."
418
- STDOUT.flush
419
- end
420
- end
421
- end
422
-
423
-
424
- end
425
-
426
- sleep(0.1)
427
- unless @pid.running?
428
- # We try to remove the pid-files by ourselves, in case the application
429
- # didn't clean it up.
430
- begin; @pid.cleanup; rescue ::Exception; end
431
-
432
- puts "#{self.group.app_name}: process with pid #{pid} successfully stopped."
433
- STDOUT.flush
434
- end
435
-
436
- end
437
-
438
- def zap
439
- @pid.cleanup
440
- end
441
-
442
- def zap!
443
- begin; @pid.cleanup; rescue ::Exception; end
444
- end
445
-
446
- def show_status
447
- running = self.running?
448
-
449
- 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 + ')' : ''}"
450
- end
451
-
452
- # This function implements a (probably too simle) method to detect
453
- # whether the program with the pid found in the pid-file is still running.
454
- # It just searches for the pid in the output of <tt>ps ax</tt>, which
455
- # is probably not a good idea in some cases.
456
- # Alternatives would be to use a direct access method the unix process control
457
- # system.
458
- #
459
- def running?
460
- if @pid.exist?
461
- return Pid.running?(@pid.pid)
462
- end
463
-
464
- return false
465
- end
466
- end
467
-
468
- end