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